diff --git a/src/index.html b/src/index.html index 9c8208a1..771d58d1 100644 --- a/src/index.html +++ b/src/index.html @@ -28,6 +28,7 @@ vertexshaderart.com +
diff --git a/src/js/main.js b/src/js/main.js index 5988708d..0d0af4a7 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -68,7 +68,7 @@ define([ "use strict"; - + const compressor = new LZMA( 'lzma_worker.js' ); // There's really no good way to tell which browsers fail. // Right now Safari doesn't expose AudioContext (it's still webkitAudioContext) @@ -79,6 +79,8 @@ define([ var $ = document.querySelector.bind(document); var gl; var m4 = twgl.m4; + const { base64 } = misc; + let s_firstCompile = true; var q = misc.parseUrlQuery(); var s = { screenshotCanvas: document.createElement("canvas"), @@ -1264,6 +1266,21 @@ define([ settings.shader = e.userData; setShaderSuccessStatus(true); clearLineErrors(); + if (s_firstCompile) { + s_firstCompile = false; + return; + } + const art = { + settings, + }; + const data = JSON.stringify(art); + compressor.compress(data, 1, function(bytes) { + const hex = base64.encode(bytes); + const params = new URLSearchParams({ + s: hex + }); + parent.history.replaceState({}, '', `/src/#${params.toString()}`); + }); }); s.programManager.on('failure', function(errors) { setShaderSuccessStatus(false); @@ -2188,6 +2205,22 @@ define([ } } + if (window.location.hash.includes("s=")) { + try { + const p = new URLSearchParams(window.location.hash.substring(1)); + const bytes = base64.decode(p.get('s')); + await new Promise((resolve, reject) => { + compressor.decompress(bytes, function(text) { + const art = JSON.parse(text); + settings = art.settings; + resolve(); + }, () => {}, reject); + }); + } catch { + settings = s.sets.default; + } + } + document.querySelectorAll('.parentLink').forEach(elem => { elem.addEventListener('click', function(event) { if (parent) { diff --git a/src/js/misc.js b/src/js/misc.js index b23207ee..e2384b76 100644 --- a/src/js/misc.js +++ b/src/js/misc.js @@ -534,10 +534,18 @@ define(function() { return a === b; } + const base64 = { + decode: s => Uint8Array.from(atob(s), c => c.charCodeAt(0)), + encode: b => btoa(String.fromCharCode(...new Uint8Array(b))), + decodeToString: s => new TextDecoder().decode(base64.decode(s)), + encodeString: s => base64.encode(new TextEncoder().encode(s)), + }; + return { applyObject: applyObject, applyUrlSettings: applyUrlSettings, applyListeners: applyListeners, + base64, clamp: clamp, clampPlusMinus: clampPlusMinus, copyProperties: copyProperties, diff --git a/src/lzma.js b/src/lzma.js new file mode 100644 index 00000000..7aec536f --- /dev/null +++ b/src/lzma.js @@ -0,0 +1,126 @@ +/// This code is licensed under the MIT License. See LICENSE for more details. + +/// Does the environment support web workers? If not, let's fake it. +if (!Worker) { + ///NOTE: IE8 needs onmessage to be created first, IE9 cannot, IE7- do not care. + /*@cc_on + /// Is this IE8-? + @if (@_jscript_version < 9) + var onmessage = function () {}; + @end + @*/ + + /// If this were a regular function statement, IE9 would run it first and therefore make the Worker variable truthy because of hoisting. + var Worker = function(script) { + var global_var, + return_object = {}; + + /// Determine the global variable (it's called "window" in browsers, "global" in Node.js). + if (typeof window !== "undefined") { + global_var = window; + } else if (global) { + global_var = global; + } + + /// Is the environment is browser? + /// If not, create a require() function, if it doesn't have one. + if (global_var.document && !global_var.require) { + global_var.require = function (path) { + var script_tag = document.createElement("script"); + script_tag.type ="text/javascript"; + script_tag.src = path; + document.getElementsByTagName('head')[0].appendChild(script_tag); + }; + } + + /// Dummy onmessage() function. + return_object.onmessage = function () {}; + + /// This is the function that the main script calls to post a message to the "worker." + return_object.postMessage = function (message) { + /// Delay the call just in case the "worker" script has not had time to load. + setTimeout(function () { + /// Call the global onmessage() created by the "worker." + ///NOTE: Wrap the message in an object. + global_var.onmessage({data: message}); + }, 10); + }; + + /// Create a global postMessage() function for the "worker" to call. + global_var.postMessage = function (e) { + ///NOTE: Wrap the message in an object. + ///TODO: Add more properties. + return_object.onmessage({data: e, type: "message"}); + }; + + require(script); + + return return_object; + }; +} + + +///NOTE: The "this" keyword is the global context ("window" variable) if loaded via a