diff --git a/src/core/Utils.js b/src/core/Utils.js index 9011880f..5a045fa5 100755 --- a/src/core/Utils.js +++ b/src/core/Utils.js @@ -981,6 +981,37 @@ const Utils = { }, + /** + * Parses URI parameters into a JSON object. + * + * @param {string} paramStr - The serialised query or hash section of a URI + * @returns {object} + * + * @example + * // returns {a: 'abc', b: '123'} + * Utils.parseURIParams("?a=abc&b=123") + * Utils.parseURIParams("#a=abc&b=123") + */ + parseURIParams: function(paramStr) { + if (paramStr === "") return {}; + + // Cut off ? or # and split on & + const params = paramStr.substr(1).split("&"); + + const result = {}; + for (let i = 0; i < params.length; i++) { + const param = params[i].split("="); + if (param.length !== 2) { + result[params[i]] = true; + } else { + result[param[0]] = decodeURIComponent(param[1].replace(/\+/g, " ")); + } + } + + return result; + }, + + /** * Actual modulo function, since % is actually the remainder function in JS. * diff --git a/src/web/App.js b/src/web/App.js index 7b02c350..5ede9ebd 100755 --- a/src/web/App.js +++ b/src/web/App.js @@ -399,39 +399,28 @@ App.prototype.addFavourite = function(name) { * Checks for input and recipe in the URI parameters and loads them if present. */ App.prototype.loadURIParams = function() { - // Load query string from URI - this.queryString = (function(a) { - if (a === "") return {}; - const b = {}; - for (let i = 0; i < a.length; i++) { - const p = a[i].split("="); - if (p.length !== 2) { - b[a[i]] = true; - } else { - b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " ")); - } - } - return b; - })(window.location.search.substr(1).split("&")); + // Load query string or hash from URI (depending on which is populated) + const params = window.location.search || window.location.hash; + this.uriParams = Utils.parseURIParams(params); // Pause auto-bake while loading but don't modify `this.autoBake_` // otherwise `manualBake` cannot trigger. this.autoBakePause = true; - // Read in recipe from query string - if (this.queryString.recipe) { + // Read in recipe from URI params + if (this.uriParams.recipe) { try { - const recipeConfig = JSON.parse(this.queryString.recipe); + const recipeConfig = JSON.parse(this.uriParams.recipe); this.setRecipeConfig(recipeConfig); } catch (err) {} - } else if (this.queryString.op) { + } else if (this.uriParams.op) { // If there's no recipe, look for single operations this.manager.recipe.clearRecipe(); try { - this.manager.recipe.addOperation(this.queryString.op); + this.manager.recipe.addOperation(this.uriParams.op); } catch (err) { // If no exact match, search for nearest match and add that - const matchedOps = this.manager.ops.filterOperations(this.queryString.op, false); + const matchedOps = this.manager.ops.filterOperations(this.uriParams.op, false); if (matchedOps.length) { this.manager.recipe.addOperation(matchedOps[0].name); } @@ -439,15 +428,15 @@ App.prototype.loadURIParams = function() { // Populate search with the string const search = document.getElementById("search"); - search.value = this.queryString.op; + search.value = this.uriParams.op; search.dispatchEvent(new Event("search")); } } - // Read in input data from query string - if (this.queryString.input) { + // Read in input data from URI params + if (this.uriParams.input) { try { - const inputData = Utils.fromBase64(this.queryString.input); + const inputData = Utils.fromBase64(this.uriParams.input); this.setInput(inputData); } catch (err) {} } diff --git a/src/web/ControlsWaiter.js b/src/web/ControlsWaiter.js index 87545df8..3d693217 100755 --- a/src/web/ControlsWaiter.js +++ b/src/web/ControlsWaiter.js @@ -174,20 +174,21 @@ ControlsWaiter.prototype.generateStateUrl = function(includeRecipe, includeInput const inputStr = Utils.toBase64(this.app.getInput(), "A-Za-z0-9+/"); // B64 alphabet with no padding includeRecipe = includeRecipe && (recipeConfig.length > 0); - includeInput = includeInput && (inputStr.length > 0) && (inputStr.length < 8000); + // Only inlcude input if it is less than 50KB (51200 * 4/3 as it is Base64 encoded) + includeInput = includeInput && (inputStr.length > 0) && (inputStr.length <= 68267); const params = [ includeRecipe ? ["recipe", recipeStr] : undefined, includeInput ? ["input", inputStr] : undefined, ]; - const query = params + const hash = params .filter(v => v) .map(([key, value]) => `${key}=${encodeURIComponent(value)}`) .join("&"); - if (query) { - return `${link}?${query}`; + if (hash) { + return `${link}#${hash}`; } return link; diff --git a/src/web/static/ga.html b/src/web/static/ga.html index f70b9c3a..e5f8db82 100644 --- a/src/web/static/ga.html +++ b/src/web/static/ga.html @@ -9,7 +9,7 @@ ga('create', 'UA-85682716-2', 'auto'); - // Specifying location.pathname here overrides the default URL which would include arguments. + // Specifying location.pathname here overrides the default URL which could include arguments. // This method prevents Google Analytics from logging any recipe or input data in the URL. ga('send', 'pageview', location.pathname);