From c39622ed1e3136d0e60108905d88a7cd2da190b1 Mon Sep 17 00:00:00 2001 From: toby Date: Fri, 21 Apr 2017 17:48:42 -0400 Subject: [PATCH 1/9] Add support for async ops using async/await --- src/core/Chef.js | 4 ++-- src/core/FlowControl.js | 4 ++-- src/core/Recipe.js | 6 +++--- src/web/App.js | 4 ++-- test/TestRegister.js | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/core/Chef.js b/src/core/Chef.js index 8192640..81eb059 100755 --- a/src/core/Chef.js +++ b/src/core/Chef.js @@ -34,7 +34,7 @@ var Chef = function() { * @returns {number} response.duration - The number of ms it took to execute the recipe * @returns {number} response.error - The error object thrown by a failed operation (false if no error) */ -Chef.prototype.bake = function(inputText, recipeConfig, options, progress, step) { +Chef.prototype.bake = async function(inputText, recipeConfig, options, progress, step) { var startTime = new Date().getTime(), recipe = new Recipe(recipeConfig), containsFc = recipe.containsFlowControl(), @@ -72,7 +72,7 @@ Chef.prototype.bake = function(inputText, recipeConfig, options, progress, step) } try { - progress = recipe.execute(this.dish, progress); + progress = await recipe.execute(this.dish, progress); } catch (err) { // Return the error in the result so that everything else gets correctly updated // rather than throwing it here and losing state info. diff --git a/src/core/FlowControl.js b/src/core/FlowControl.js index 54541f7..015e590 100755 --- a/src/core/FlowControl.js +++ b/src/core/FlowControl.js @@ -38,7 +38,7 @@ const FlowControl = { * @param {Operation[]} state.opList - The list of operations in the recipe. * @returns {Object} The updated state of the recipe. */ - runFork: function(state) { + runFork: async function(state) { var opList = state.opList, inputType = opList[state.progress].inputType, outputType = opList[state.progress].outputType, @@ -73,7 +73,7 @@ const FlowControl = { for (i = 0; i < inputs.length; i++) { var dish = new Dish(inputs[i], inputType); try { - progress = recipe.execute(dish, 0); + progress = await recipe.execute(dish, 0); } catch (err) { if (!ignoreErrors) { throw err; diff --git a/src/core/Recipe.js b/src/core/Recipe.js index b0a3967..ed8c19b 100755 --- a/src/core/Recipe.js +++ b/src/core/Recipe.js @@ -145,7 +145,7 @@ Recipe.prototype.lastOpIndex = function(startIndex) { * @param {number} [startFrom=0] - The index of the Operation to start executing from * @returns {number} - The final progress through the recipe */ -Recipe.prototype.execute = function(dish, startFrom) { +Recipe.prototype.execute = async function(dish, startFrom) { startFrom = startFrom || 0; var op, input, output, numJumps = 0; @@ -170,11 +170,11 @@ Recipe.prototype.execute = function(dish, startFrom) { "numJumps" : numJumps }; - state = op.run(state); + state = await op.run(state); i = state.progress; numJumps = state.numJumps; } else { - output = op.run(input, op.getIngValues()); + output = await op.run(input, op.getIngValues()); dish.set(output, op.outputType); } } catch (err) { diff --git a/src/web/App.js b/src/web/App.js index 59121bd..945b28e 100755 --- a/src/web/App.js +++ b/src/web/App.js @@ -73,11 +73,11 @@ App.prototype.handleError = function(err) { * @param {boolean} [step] - Set to true if we should only execute one operation instead of the * whole recipe. */ -App.prototype.bake = function(step) { +App.prototype.bake = async function(step) { var response; try { - response = this.chef.bake( + response = await this.chef.bake( this.getInput(), // The user's input this.getRecipeConfig(), // The configuration of the recipe this.options, // Options set by the user diff --git a/test/TestRegister.js b/test/TestRegister.js index fdd3431..bfdb697 100644 --- a/test/TestRegister.js +++ b/test/TestRegister.js @@ -40,13 +40,13 @@ import Chef from "../src/core/Chef.js"; this.tests.map(function(test, i) { var chef = new Chef(); - return Promise.resolve(chef.bake( + return chef.bake( test.input, test.recipeConfig, {}, 0, false - )) + ) .then(function(result) { var ret = { test: test, From 02f855ff096a8d0210c97fbe1113267c261c5f93 Mon Sep 17 00:00:00 2001 From: toby Date: Fri, 21 Apr 2017 17:49:10 -0400 Subject: [PATCH 2/9] Add more tests for flow control ops --- test/tests/operations/FlowControl.js | 133 +++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/test/tests/operations/FlowControl.js b/test/tests/operations/FlowControl.js index 96ae2e8..985481c 100644 --- a/test/tests/operations/FlowControl.js +++ b/test/tests/operations/FlowControl.js @@ -66,6 +66,62 @@ TestRegister.addTests([ {"op":"To Base64", "args":["A-Za-z0-9+/="]} ] }, + { + name: "Jump: skips 0", + input: [ + "should be changed", + ].join("\n"), + expectedOutput: [ + "should be changed was changed", + ].join("\n"), + recipeConfig: [ + { + op: "Jump", + args: [0, 10], + }, + { + op: "Find / Replace", + args: [ + { + "option": "Regex", + "string": "should be changed" + }, + "should be changed was changed", + true, + true, + true, + ], + }, + ], + }, + { + name: "Jump: skips 1", + input: [ + "shouldnt be changed", + ].join("\n"), + expectedOutput: [ + "shouldnt be changed", + ].join("\n"), + recipeConfig: [ + { + op: "Jump", + args: [1, 10], + }, + { + op: "Find / Replace", + args: [ + { + "option": "Regex", + "string": "shouldnt be changed" + }, + "shouldnt be changed was changed", + true, + true, + true, + ], + }, + ], + }, { name: "Conditional Jump: Skips 0", input: [ @@ -111,4 +167,81 @@ TestRegister.addTests([ }, ], }, + { + name: "Conditional Jump: Skips 1", + input: [ + "match", + "should not be changed", + "should be changed", + ].join("\n"), + expectedOutput: [ + "match", + "should not be changed", + "should be changed was changed" + ].join("\n"), + recipeConfig: [ + { + op: "Conditional Jump", + args: ["match", 1, 10], + }, + { + op: "Find / Replace", + args: [ + { + "option": "Regex", + "string": "should not be changed" + }, + "should not be changed was changed", + true, + true, + true, + ], + }, + { + op: "Find / Replace", + args: [ + { + "option": "Regex", + "string": "should be changed" + }, + "should be changed was changed", + true, + true, + true, + ], + }, + ], + }, + { + name: "Conditional Jump: Skips negatively", + input: [ + "match", + ].join("\n"), + expectedOutput: [ + "replaced", + ].join("\n"), + recipeConfig: [ + { + op: "Jump", + args: [1], + }, + { + op: "Find / Replace", + args: [ + { + "option": "Regex", + "string": "match" + }, + "replaced", + true, + true, + true, + ], + }, + { + op: "Conditional Jump", + args: ["match", -2, 10], + }, + ], + }, ]); From 9f60dc3dd6ec364ca0d422b2d674c67ec7cb20d0 Mon Sep 17 00:00:00 2001 From: toby Date: Fri, 21 Apr 2017 17:56:16 -0400 Subject: [PATCH 3/9] Change ecmaVersion to 8 to make eslint happy --- src/.eslintrc.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/.eslintrc.json b/src/.eslintrc.json index 677146b..2f48f1d 100755 --- a/src/.eslintrc.json +++ b/src/.eslintrc.json @@ -1,6 +1,6 @@ { "parserOptions": { - "ecmaVersion": 6, + "ecmaVersion": 8, "ecmaFeatures": { "impliedStrict": true }, From 3fb660d8164b4b21969e6ab616bdb4255e841aad Mon Sep 17 00:00:00 2001 From: toby Date: Fri, 21 Apr 2017 18:05:30 -0400 Subject: [PATCH 4/9] Add jsdoc-babel plugin This is to stop jsdoc parsing errors. More information in this thread: https://github.com/jsdoc3/jsdoc/issues/555 --- docs/jsdoc.conf.json | 5 ++++- package.json | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/jsdoc.conf.json b/docs/jsdoc.conf.json index 00a85cc..3c247ed 100755 --- a/docs/jsdoc.conf.json +++ b/docs/jsdoc.conf.json @@ -2,7 +2,10 @@ "tags": { "allowUnknownTags": true }, - "plugins": ["plugins/markdown"], + "plugins": [ + "plugins/markdown", + "node_modules/jsdoc-babel" + ], "templates": { "systemName": "CyberChef", "footer": "", diff --git a/package.json b/package.json index 9381cf0..8f41b1e 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "html-webpack-plugin": "^2.28.0", "imports-loader": "^0.7.1", "ink-docstrap": "^1.1.4", + "jsdoc-babel": "^0.3.0", "less": "^2.7.2", "less-loader": "^4.0.2", "style-loader": "^0.15.0", From a13e2468db234acbb67b0587b46dd5df29c92957 Mon Sep 17 00:00:00 2001 From: toby Date: Fri, 21 Apr 2017 20:04:12 -0400 Subject: [PATCH 5/9] Added UI loading indications to the HTML app --- src/web/App.js | 38 ++++++++++++++++++++++++++++++++ src/web/css/structure/layout.css | 33 +++++++++++++++++++++++++++ src/web/html/index.html | 2 ++ 3 files changed, 73 insertions(+) diff --git a/src/web/App.js b/src/web/App.js index 945b28e..9c45765 100755 --- a/src/web/App.js +++ b/src/web/App.js @@ -30,6 +30,7 @@ var App = function(categories, operations, defaultFavourites, defaultOptions) { this.chef = new Chef(); this.manager = new Manager(this); + this.baking = false; this.autoBake_ = false; this.progress = 0; this.ingId = 0; @@ -67,6 +68,37 @@ App.prototype.handleError = function(err) { }; +/** + * Updates the UI to show if baking is in process or not. + * + * @param {bakingStatus} + */ +App.prototype.setBakingStatus = function(bakingStatus) { + this.baking = bakingStatus; + + var inputLoadingIcon = document.querySelector("#input .title .loading-icon"), + outputLoadingIcon = document.querySelector("#output .title .loading-icon"), + inputElement = document.querySelector("#input-text"), + outputElement = document.querySelector("#output-text"); + + if (bakingStatus) { + inputLoadingIcon.style.display = "inline-block"; + outputLoadingIcon.style.display = "inline-block"; + inputElement.classList.add("disabled"); + outputElement.classList.add("disabled"); + inputElement.disabled = true; + outputElement.disabled = true; + } else { + inputLoadingIcon.style.display = "none"; + outputLoadingIcon.style.display = "none"; + inputElement.classList.remove("disabled"); + outputElement.classList.remove("disabled"); + inputElement.disabled = false; + outputElement.disabled = false; + } +}; + + /** * Calls the Chef to bake the current input using the current recipe. * @@ -76,6 +108,10 @@ App.prototype.handleError = function(err) { App.prototype.bake = async function(step) { var response; + if (this.baking) return; + + this.setBakingStatus(true); + try { response = await this.chef.bake( this.getInput(), // The user's input @@ -94,6 +130,8 @@ App.prototype.bake = async function(step) { this.handleError(response.error); } + this.setBakingStatus(false); + this.options = response.options; this.dishStr = response.type === "html" ? Utils.stripHtmlTags(response.result, true) : response.result; this.progress = response.progress; diff --git a/src/web/css/structure/layout.css b/src/web/css/structure/layout.css index 8286eb8..3cd5abe 100755 --- a/src/web/css/structure/layout.css +++ b/src/web/css/structure/layout.css @@ -430,3 +430,36 @@ span.btn img { border-top: none; margin-top: 0; } + + +@-moz-keyframes spinner { + from { -moz-transform: rotate(0deg); } + to { -moz-transform: rotate(359deg); } +} +@-webkit-keyframes spinner { + from { -webkit-transform: rotate(0deg); } + to { -webkit-transform: rotate(359deg); } +} +@keyframes spinner { + from {transform:rotate(0deg);} + to {transform:rotate(359deg);} +} + +.loading-icon::before { + content: "\21bb"; +} + +.loading-icon { + -webkit-animation-name: spinner; + -webkit-animation-duration: 1000ms; + -webkit-animation-iteration-count: infinite; + -webkit-animation-timing-function: linear; + -moz-animation-name: spinner; + -moz-animation-duration: 1000ms; + -moz-animation-iteration-count: infinite; + -moz-animation-timing-function: linear; + -ms-animation-name: spinner; + -ms-animation-duration: 1000ms; + -ms-animation-iteration-count: infinite; + -ms-animation-timing-function: linear; +} diff --git a/src/web/html/index.html b/src/web/html/index.html index 35877e4..afe56fa 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -100,6 +100,7 @@
+
@@ -116,6 +117,7 @@
+
From 21c0fed8339eb2921e456f2dc298920eff3d28c5 Mon Sep 17 00:00:00 2001 From: toby Date: Sun, 23 Apr 2017 13:14:59 -0400 Subject: [PATCH 6/9] Fix bug: baking error did not reset baking status --- src/web/App.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web/App.js b/src/web/App.js index 9c45765..b4fb3a7 100755 --- a/src/web/App.js +++ b/src/web/App.js @@ -124,14 +124,14 @@ App.prototype.bake = async function(step) { this.handleError(err); } + this.setBakingStatus(false); + if (!response) return; if (response.error) { this.handleError(response.error); } - this.setBakingStatus(false); - this.options = response.options; this.dishStr = response.type === "html" ? Utils.stripHtmlTags(response.result, true) : response.result; this.progress = response.progress; From 50784f260082677d31a4a7f48ee47a1bfddc5a4e Mon Sep 17 00:00:00 2001 From: toby Date: Sun, 23 Apr 2017 13:41:28 -0400 Subject: [PATCH 7/9] Debounce autobake in the web app. Added debounce with guidance from the underscore.js implementation: https://github.com/jashkenas/underscore/blob/e944e0275abb3e1f366417ba8facb5754a7ad273/underscore.js#L880 --- src/core/Utils.js | 27 +++++++++++++++++++++++++++ src/web/App.js | 2 ++ 2 files changed, 29 insertions(+) diff --git a/src/core/Utils.js b/src/core/Utils.js index 2e4b354..a49565e 100755 --- a/src/core/Utils.js +++ b/src/core/Utils.js @@ -1185,6 +1185,33 @@ const Utils = { "Latin1": CryptoJS.enc.Latin1, }, + + /** + * A utility for "debouncing" functions. + * Debouncing is when you want to ensure events triggered by an event are rate-limited. + * @constant + */ + debounce(fn, delay) { + let timeout; + + return function() { + /** + * later calls the debounced function with arguments. + * If the debounced function is called again, then the timeout + * which calls later is cancelled. + */ + let later = () => { + fn.apply(this, arguments); + }; + + if (timeout) { + clearTimeout(timeout); + } + + timeout = setTimeout(later, delay); + }; + }, + }; export default Utils; diff --git a/src/web/App.js b/src/web/App.js index b4fb3a7..de8af4a 100755 --- a/src/web/App.js +++ b/src/web/App.js @@ -36,6 +36,8 @@ var App = function(categories, operations, defaultFavourites, defaultOptions) { this.ingId = 0; window.chef = this.chef; + + this.autoBake = Utils.debounce(this.autoBake, 300); }; From 6122e33f4faf58c8c0ecf24267a60978079ac598 Mon Sep 17 00:00:00 2001 From: toby Date: Wed, 3 May 2017 10:27:13 -0400 Subject: [PATCH 8/9] Removed debounced autobake & stop disabling input --- src/web/App.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/web/App.js b/src/web/App.js index de8af4a..4ab96b0 100755 --- a/src/web/App.js +++ b/src/web/App.js @@ -36,8 +36,6 @@ var App = function(categories, operations, defaultFavourites, defaultOptions) { this.ingId = 0; window.chef = this.chef; - - this.autoBake = Utils.debounce(this.autoBake, 300); }; @@ -80,22 +78,17 @@ App.prototype.setBakingStatus = function(bakingStatus) { var inputLoadingIcon = document.querySelector("#input .title .loading-icon"), outputLoadingIcon = document.querySelector("#output .title .loading-icon"), - inputElement = document.querySelector("#input-text"), outputElement = document.querySelector("#output-text"); if (bakingStatus) { inputLoadingIcon.style.display = "inline-block"; outputLoadingIcon.style.display = "inline-block"; - inputElement.classList.add("disabled"); outputElement.classList.add("disabled"); - inputElement.disabled = true; outputElement.disabled = true; } else { inputLoadingIcon.style.display = "none"; outputLoadingIcon.style.display = "none"; - inputElement.classList.remove("disabled"); outputElement.classList.remove("disabled"); - inputElement.disabled = false; outputElement.disabled = false; } }; From 274e1139fab94940464a1e1d9c776a9fb2335e62 Mon Sep 17 00:00:00 2001 From: toby Date: Wed, 3 May 2017 10:43:30 -0400 Subject: [PATCH 9/9] Remove debounce from Utils --- src/core/Utils.js | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/core/Utils.js b/src/core/Utils.js index a49565e..2e4b354 100755 --- a/src/core/Utils.js +++ b/src/core/Utils.js @@ -1185,33 +1185,6 @@ const Utils = { "Latin1": CryptoJS.enc.Latin1, }, - - /** - * A utility for "debouncing" functions. - * Debouncing is when you want to ensure events triggered by an event are rate-limited. - * @constant - */ - debounce(fn, delay) { - let timeout; - - return function() { - /** - * later calls the debounced function with arguments. - * If the debounced function is called again, then the timeout - * which calls later is cancelled. - */ - let later = () => { - fn.apply(this, arguments); - }; - - if (timeout) { - clearTimeout(timeout); - } - - timeout = setTimeout(later, delay); - }; - }, - }; export default Utils;