2017-03-23 18:52:20 +01:00
|
|
|
import Utils from "../core/Utils.js";
|
|
|
|
import Chef from "../core/Chef.js";
|
|
|
|
import Manager from "./Manager.js";
|
|
|
|
import HTMLCategory from "./HTMLCategory.js";
|
|
|
|
import HTMLOperation from "./HTMLOperation.js";
|
|
|
|
import Split from "split.js";
|
2017-03-21 23:41:44 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* HTML view for CyberChef responsible for building the web page and dealing with all user
|
|
|
|
* interactions.
|
|
|
|
*
|
|
|
|
* @author n1474335 [n1474335@gmail.com]
|
|
|
|
* @copyright Crown Copyright 2016
|
|
|
|
* @license Apache-2.0
|
|
|
|
*
|
|
|
|
* @constructor
|
|
|
|
* @param {CatConf[]} categories - The list of categories and operations to be populated.
|
|
|
|
* @param {Object.<string, OpConf>} operations - The list of operation configuration objects.
|
2017-01-31 19:24:56 +01:00
|
|
|
* @param {String[]} defaultFavourites - A list of default favourite operations.
|
2016-11-28 11:42:58 +01:00
|
|
|
* @param {Object} options - Default setting for app options.
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
var App = function(categories, operations, defaultFavourites, defaultOptions) {
|
2016-11-28 11:42:58 +01:00
|
|
|
this.categories = categories;
|
|
|
|
this.operations = operations;
|
2017-01-31 19:24:56 +01:00
|
|
|
this.dfavourites = defaultFavourites;
|
|
|
|
this.doptions = defaultOptions;
|
|
|
|
this.options = Utils.extend({}, defaultOptions);
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
this.chef = new Chef();
|
|
|
|
this.manager = new Manager(this);
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
this.autoBake_ = false;
|
2016-11-28 11:42:58 +01:00
|
|
|
this.progress = 0;
|
2017-01-31 19:24:56 +01:00
|
|
|
this.ingId = 0;
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
window.chef = this.chef;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function sets up the stage and creates listeners for all events.
|
|
|
|
*
|
|
|
|
* @fires Manager#appstart
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.setup = function() {
|
2016-11-28 11:42:58 +01:00
|
|
|
document.dispatchEvent(this.manager.appstart);
|
2017-01-31 19:24:56 +01:00
|
|
|
this.initialiseSplitter();
|
|
|
|
this.loadLocalStorage();
|
|
|
|
this.populateOperationsList();
|
2016-11-28 11:42:58 +01:00
|
|
|
this.manager.setup();
|
2017-01-31 19:24:56 +01:00
|
|
|
this.resetLayout();
|
|
|
|
this.setCompileMessage();
|
|
|
|
this.loadURIParams();
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An error handler for displaying the error to the user.
|
|
|
|
*
|
|
|
|
* @param {Error} err
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.handleError = function(err) {
|
2016-11-28 11:42:58 +01:00
|
|
|
console.error(err);
|
2017-01-31 19:24:56 +01:00
|
|
|
var msg = err.displayStr || err.toString();
|
|
|
|
this.alert(msg, "danger", this.options.errorTimeout, !this.options.showErrors);
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calls the Chef to bake the current input using the current recipe.
|
|
|
|
*
|
|
|
|
* @param {boolean} [step] - Set to true if we should only execute one operation instead of the
|
|
|
|
* whole recipe.
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.bake = function(step) {
|
2016-11-28 11:42:58 +01:00
|
|
|
var response;
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
try {
|
|
|
|
response = this.chef.bake(
|
2017-01-31 19:24:56 +01:00
|
|
|
this.getInput(), // The user's input
|
|
|
|
this.getRecipeConfig(), // The configuration of the recipe
|
2016-11-28 11:42:58 +01:00
|
|
|
this.options, // Options set by the user
|
|
|
|
this.progress, // The current position in the recipe
|
|
|
|
step // Whether or not to take one step or execute the whole recipe
|
|
|
|
);
|
|
|
|
} catch (err) {
|
2017-01-31 19:24:56 +01:00
|
|
|
this.handleError(err);
|
2016-12-14 17:39:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!response) return;
|
|
|
|
|
|
|
|
if (response.error) {
|
2017-01-31 19:24:56 +01:00
|
|
|
this.handleError(response.error);
|
2016-12-14 17:39:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
this.options = response.options;
|
2017-01-31 19:24:56 +01:00
|
|
|
this.dishStr = response.type === "html" ? Utils.stripHtmlTags(response.result, true) : response.result;
|
2016-12-14 17:39:17 +01:00
|
|
|
this.progress = response.progress;
|
2017-01-31 19:24:56 +01:00
|
|
|
this.manager.recipe.updateBreakpointIndicator(response.progress);
|
2016-12-14 17:39:17 +01:00
|
|
|
this.manager.output.set(response.result, response.type, response.duration);
|
|
|
|
|
|
|
|
// If baking took too long, disable auto-bake
|
2017-01-31 19:24:56 +01:00
|
|
|
if (response.duration > this.options.autoBakeThreshold && this.autoBake_) {
|
|
|
|
this.manager.controls.setAutoBake(false);
|
|
|
|
this.alert("Baking took longer than " + this.options.autoBakeThreshold +
|
2016-12-14 17:39:17 +01:00
|
|
|
"ms, Auto Bake has been disabled.", "warning", 5000);
|
2016-11-28 11:42:58 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Runs Auto Bake if it is set.
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.autoBake = function() {
|
2017-01-31 19:24:56 +01:00
|
|
|
if (this.autoBake_) {
|
2016-11-28 11:42:58 +01:00
|
|
|
this.bake();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Runs a silent bake forcing the browser to load and cache all the relevant JavaScript code needed
|
|
|
|
* to do a real bake.
|
|
|
|
*
|
|
|
|
* The output will not be modified (hence "silent" bake). This will only actually execute the
|
|
|
|
* recipe if auto-bake is enabled, otherwise it will just load the recipe, ingredients and dish.
|
|
|
|
*
|
|
|
|
* @returns {number} - The number of miliseconds it took to run the silent bake.
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.silentBake = function() {
|
2017-01-31 19:24:56 +01:00
|
|
|
var startTime = new Date().getTime(),
|
|
|
|
recipeConfig = this.getRecipeConfig();
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
if (this.autoBake_) {
|
|
|
|
this.chef.silentBake(recipeConfig);
|
2016-11-28 11:42:58 +01:00
|
|
|
}
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
return new Date().getTime() - startTime;
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the user's input data.
|
|
|
|
*
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.getInput = function() {
|
2016-11-28 11:42:58 +01:00
|
|
|
var input = this.manager.input.get();
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
// Save to session storage in case we need to restore it later
|
2017-01-31 19:24:56 +01:00
|
|
|
sessionStorage.setItem("inputLength", input.length);
|
2016-11-28 11:42:58 +01:00
|
|
|
sessionStorage.setItem("input", input);
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
return input;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the user's input data.
|
|
|
|
*
|
|
|
|
* @param {string} input - The string to set the input to
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.setInput = function(input) {
|
2017-01-31 19:24:56 +01:00
|
|
|
sessionStorage.setItem("inputLength", input.length);
|
2016-11-28 11:42:58 +01:00
|
|
|
sessionStorage.setItem("input", input);
|
|
|
|
this.manager.input.set(input);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Populates the operations accordion list with the categories and operations specified in the
|
|
|
|
* view constructor.
|
|
|
|
*
|
|
|
|
* @fires Manager#oplistcreate
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.populateOperationsList = function() {
|
2016-11-28 11:42:58 +01:00
|
|
|
// Move edit button away before we overwrite it
|
|
|
|
document.body.appendChild(document.getElementById("edit-favourites"));
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
var html = "";
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
for (var i = 0; i < this.categories.length; i++) {
|
2017-01-31 19:24:56 +01:00
|
|
|
var catConf = this.categories[i],
|
2016-11-28 11:42:58 +01:00
|
|
|
selected = i === 0,
|
2017-01-31 19:24:56 +01:00
|
|
|
cat = new HTMLCategory(catConf.name, selected);
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
for (var j = 0; j < catConf.ops.length; j++) {
|
|
|
|
var opName = catConf.ops[j],
|
|
|
|
op = new HTMLOperation(opName, this.operations[opName], this, this.manager);
|
|
|
|
cat.addOperation(op);
|
2016-11-28 11:42:58 +01:00
|
|
|
}
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
html += cat.toHtml();
|
2016-11-28 11:42:58 +01:00
|
|
|
}
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
document.getElementById("categories").innerHTML = html;
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
var opLists = document.querySelectorAll("#categories .op-list");
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
for (i = 0; i < opLists.length; i++) {
|
|
|
|
opLists[i].dispatchEvent(this.manager.oplistcreate);
|
2016-11-28 11:42:58 +01:00
|
|
|
}
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
// Add edit button to first category (Favourites)
|
|
|
|
document.querySelector("#categories a").appendChild(document.getElementById("edit-favourites"));
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets up the adjustable splitter to allow the user to resize areas of the page.
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.initialiseSplitter = function() {
|
2017-01-31 19:24:56 +01:00
|
|
|
this.columnSplitter = Split(["#operations", "#recipe", "#IO"], {
|
2016-11-28 11:42:58 +01:00
|
|
|
sizes: [20, 30, 50],
|
2016-12-21 13:13:03 +01:00
|
|
|
minSize: [240, 325, 440],
|
2016-11-28 11:42:58 +01:00
|
|
|
gutterSize: 4,
|
2016-12-21 13:13:03 +01:00
|
|
|
onDrag: function() {
|
2017-01-31 19:24:56 +01:00
|
|
|
this.manager.controls.adjustWidth();
|
|
|
|
this.manager.output.adjustWidth();
|
2016-12-21 13:13:03 +01:00
|
|
|
}.bind(this)
|
2016-11-28 11:42:58 +01:00
|
|
|
});
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
this.ioSplitter = Split(["#input", "#output"], {
|
2016-11-28 11:42:58 +01:00
|
|
|
direction: "vertical",
|
|
|
|
gutterSize: 4,
|
|
|
|
});
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
this.resetLayout();
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Loads the information previously saved to the HTML5 local storage object so that user options
|
|
|
|
* and favourites can be restored.
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.loadLocalStorage = function() {
|
2016-11-28 11:42:58 +01:00
|
|
|
// Load options
|
2017-01-31 19:24:56 +01:00
|
|
|
var lOptions;
|
2016-11-28 11:42:58 +01:00
|
|
|
if (localStorage.options !== undefined) {
|
2017-01-31 19:24:56 +01:00
|
|
|
lOptions = JSON.parse(localStorage.options);
|
2016-11-28 11:42:58 +01:00
|
|
|
}
|
2017-01-31 19:24:56 +01:00
|
|
|
this.manager.options.load(lOptions);
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
// Load favourites
|
2017-01-31 19:24:56 +01:00
|
|
|
this.loadFavourites();
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Loads the user's favourite operations from the HTML5 local storage object and populates the
|
|
|
|
* Favourites category with them.
|
|
|
|
* If the user currently has no saved favourites, the defaults from the view constructor are used.
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.loadFavourites = function() {
|
2016-11-28 11:42:58 +01:00
|
|
|
var favourites = localStorage.favourites &&
|
|
|
|
localStorage.favourites.length > 2 ?
|
|
|
|
JSON.parse(localStorage.favourites) :
|
|
|
|
this.dfavourites;
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
favourites = this.validFavourites(favourites);
|
|
|
|
this.saveFavourites(favourites);
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
var favCat = this.categories.filter(function(c) {
|
2016-12-14 17:39:17 +01:00
|
|
|
return c.name === "Favourites";
|
2016-11-28 11:42:58 +01:00
|
|
|
})[0];
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
if (favCat) {
|
|
|
|
favCat.ops = favourites;
|
2016-11-28 11:42:58 +01:00
|
|
|
} else {
|
|
|
|
this.categories.unshift({
|
|
|
|
name: "Favourites",
|
|
|
|
ops: favourites
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Filters the list of favourite operations that the user had stored and removes any that are no
|
|
|
|
* longer available. The user is notified if this is the case.
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
* @param {string[]} favourites - A list of the user's favourite operations
|
|
|
|
* @returns {string[]} A list of the valid favourites
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.validFavourites = function(favourites) {
|
2017-01-31 19:24:56 +01:00
|
|
|
var validFavs = [];
|
2016-11-28 11:42:58 +01:00
|
|
|
for (var i = 0; i < favourites.length; i++) {
|
|
|
|
if (this.operations.hasOwnProperty(favourites[i])) {
|
2017-01-31 19:24:56 +01:00
|
|
|
validFavs.push(favourites[i]);
|
2016-11-28 11:42:58 +01:00
|
|
|
} else {
|
2017-01-31 19:24:56 +01:00
|
|
|
this.alert("The operation \"" + Utils.escapeHtml(favourites[i]) +
|
2016-12-01 00:11:06 +01:00
|
|
|
"\" is no longer available. It has been removed from your favourites.", "info");
|
2016-11-28 11:42:58 +01:00
|
|
|
}
|
|
|
|
}
|
2017-01-31 19:24:56 +01:00
|
|
|
return validFavs;
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Saves a list of favourite operations to the HTML5 local storage object.
|
|
|
|
*
|
|
|
|
* @param {string[]} favourites - A list of the user's favourite operations
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.saveFavourites = function(favourites) {
|
2017-01-31 19:24:56 +01:00
|
|
|
localStorage.setItem("favourites", JSON.stringify(this.validFavourites(favourites)));
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resets favourite operations back to the default as specified in the view constructor and
|
|
|
|
* refreshes the operation list.
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.resetFavourites = function() {
|
2017-01-31 19:24:56 +01:00
|
|
|
this.saveFavourites(this.dfavourites);
|
|
|
|
this.loadFavourites();
|
|
|
|
this.populateOperationsList();
|
|
|
|
this.manager.recipe.initialiseOperationDragNDrop();
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds an operation to the user's favourites.
|
|
|
|
*
|
|
|
|
* @param {string} name - The name of the operation
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.addFavourite = function(name) {
|
2016-11-28 11:42:58 +01:00
|
|
|
var favourites = JSON.parse(localStorage.favourites);
|
|
|
|
|
|
|
|
if (favourites.indexOf(name) >= 0) {
|
|
|
|
this.alert("'" + name + "' is already in your favourites", "info", 2000);
|
|
|
|
return;
|
|
|
|
}
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
favourites.push(name);
|
2017-01-31 19:24:56 +01:00
|
|
|
this.saveFavourites(favourites);
|
|
|
|
this.loadFavourites();
|
|
|
|
this.populateOperationsList();
|
|
|
|
this.manager.recipe.initialiseOperationDragNDrop();
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks for input and recipe in the URI parameters and loads them if present.
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.loadURIParams = function() {
|
2016-11-28 11:42:58 +01:00
|
|
|
// Load query string from URI
|
2017-01-31 19:24:56 +01:00
|
|
|
this.queryString = (function(a) {
|
2016-11-28 11:42:58 +01:00
|
|
|
if (a === "") return {};
|
|
|
|
var b = {};
|
|
|
|
for (var i = 0; i < a.length; i++) {
|
2016-12-14 17:39:17 +01:00
|
|
|
var p = a[i].split("=");
|
|
|
|
if (p.length !== 2) {
|
2016-11-28 11:42:58 +01:00
|
|
|
b[a[i]] = true;
|
|
|
|
} else {
|
|
|
|
b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return b;
|
2016-12-14 17:39:17 +01:00
|
|
|
})(window.location.search.substr(1).split("&"));
|
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
// Turn off auto-bake while loading
|
2017-01-31 19:24:56 +01:00
|
|
|
var autoBakeVal = this.autoBake_;
|
|
|
|
this.autoBake_ = false;
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
// Read in recipe from query string
|
2017-01-31 19:24:56 +01:00
|
|
|
if (this.queryString.recipe) {
|
2016-11-28 11:42:58 +01:00
|
|
|
try {
|
2017-01-31 19:24:56 +01:00
|
|
|
var recipeConfig = JSON.parse(this.queryString.recipe);
|
|
|
|
this.setRecipeConfig(recipeConfig);
|
2017-02-09 16:09:33 +01:00
|
|
|
} catch (err) {}
|
2017-01-31 19:24:56 +01:00
|
|
|
} else if (this.queryString.op) {
|
2016-11-28 11:42:58 +01:00
|
|
|
// If there's no recipe, look for single operations
|
2017-01-31 19:24:56 +01:00
|
|
|
this.manager.recipe.clearRecipe();
|
2016-11-28 11:42:58 +01:00
|
|
|
try {
|
2017-01-31 19:24:56 +01:00
|
|
|
this.manager.recipe.addOperation(this.queryString.op);
|
2017-02-09 16:09:33 +01:00
|
|
|
} catch (err) {
|
2016-11-28 11:42:58 +01:00
|
|
|
// If no exact match, search for nearest match and add that
|
2017-01-31 19:24:56 +01:00
|
|
|
var matchedOps = this.manager.ops.filterOperations(this.queryString.op, false);
|
|
|
|
if (matchedOps.length) {
|
|
|
|
this.manager.recipe.addOperation(matchedOps[0].name);
|
2016-11-28 11:42:58 +01:00
|
|
|
}
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
// Populate search with the string
|
|
|
|
var search = document.getElementById("search");
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
search.value = this.queryString.op;
|
2016-11-28 11:42:58 +01:00
|
|
|
search.dispatchEvent(new Event("search"));
|
|
|
|
}
|
|
|
|
}
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
// Read in input data from query string
|
2017-01-31 19:24:56 +01:00
|
|
|
if (this.queryString.input) {
|
2016-11-28 11:42:58 +01:00
|
|
|
try {
|
2017-01-31 19:24:56 +01:00
|
|
|
var inputData = Utils.fromBase64(this.queryString.input);
|
|
|
|
this.setInput(inputData);
|
2017-02-09 16:09:33 +01:00
|
|
|
} catch (err) {}
|
2016-11-28 11:42:58 +01:00
|
|
|
}
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
// Restore auto-bake state
|
2017-01-31 19:24:56 +01:00
|
|
|
this.autoBake_ = autoBakeVal;
|
|
|
|
this.autoBake();
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the next ingredient ID and increments it for next time.
|
|
|
|
*
|
|
|
|
* @returns {number}
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.nextIngId = function() {
|
2017-01-31 19:24:56 +01:00
|
|
|
return this.ingId++;
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the current recipe configuration.
|
|
|
|
*
|
|
|
|
* @returns {Object[]}
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.getRecipeConfig = function() {
|
2017-01-31 19:24:56 +01:00
|
|
|
var recipeConfig = this.manager.recipe.getConfig();
|
|
|
|
sessionStorage.setItem("recipeConfig", JSON.stringify(recipeConfig));
|
|
|
|
return recipeConfig;
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a recipe configuration, sets the recipe to that configuration.
|
|
|
|
*
|
2017-01-31 19:24:56 +01:00
|
|
|
* @param {Object[]} recipeConfig - The recipe configuration
|
2016-11-28 11:42:58 +01:00
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.setRecipeConfig = function(recipeConfig) {
|
2017-01-31 19:24:56 +01:00
|
|
|
sessionStorage.setItem("recipeConfig", JSON.stringify(recipeConfig));
|
|
|
|
document.getElementById("rec-list").innerHTML = null;
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
for (var i = 0; i < recipeConfig.length; i++) {
|
|
|
|
var item = this.manager.recipe.addOperation(recipeConfig[i].op);
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
// Populate arguments
|
|
|
|
var args = item.querySelectorAll(".arg");
|
|
|
|
for (var j = 0; j < args.length; j++) {
|
2016-12-14 17:39:17 +01:00
|
|
|
if (args[j].getAttribute("type") === "checkbox") {
|
2016-11-28 11:42:58 +01:00
|
|
|
// checkbox
|
2017-01-31 19:24:56 +01:00
|
|
|
args[j].checked = recipeConfig[i].args[j];
|
2016-11-28 11:42:58 +01:00
|
|
|
} else if (args[j].classList.contains("toggle-string")) {
|
2017-01-31 19:24:56 +01:00
|
|
|
// toggleString
|
|
|
|
args[j].value = recipeConfig[i].args[j].string;
|
2016-11-30 20:33:20 +01:00
|
|
|
args[j].previousSibling.children[0].innerHTML =
|
2017-01-31 19:24:56 +01:00
|
|
|
Utils.escapeHtml(recipeConfig[i].args[j].option) +
|
2016-11-28 11:42:58 +01:00
|
|
|
" <span class='caret'></span>";
|
|
|
|
} else {
|
|
|
|
// all others
|
2017-01-31 19:24:56 +01:00
|
|
|
args[j].value = recipeConfig[i].args[j];
|
2016-11-28 11:42:58 +01:00
|
|
|
}
|
|
|
|
}
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
// Set disabled and breakpoint
|
2017-01-31 19:24:56 +01:00
|
|
|
if (recipeConfig[i].disabled) {
|
2016-11-28 11:42:58 +01:00
|
|
|
item.querySelector(".disable-icon").click();
|
|
|
|
}
|
2017-01-31 19:24:56 +01:00
|
|
|
if (recipeConfig[i].breakpoint) {
|
2016-11-28 11:42:58 +01:00
|
|
|
item.querySelector(".breakpoint").click();
|
|
|
|
}
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
this.progress = 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resets the splitter positions to default.
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.resetLayout = function() {
|
2017-01-31 19:24:56 +01:00
|
|
|
this.columnSplitter.setSizes([20, 30, 50]);
|
|
|
|
this.ioSplitter.setSizes([50, 50]);
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
this.manager.controls.adjustWidth();
|
|
|
|
this.manager.output.adjustWidth();
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the compile message.
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.setCompileMessage = function() {
|
2016-11-28 11:42:58 +01:00
|
|
|
// Display time since last build and compile message
|
|
|
|
var now = new Date(),
|
2017-01-31 19:24:56 +01:00
|
|
|
timeSinceCompile = Utils.fuzzyTime(now.getTime() - window.compileTime),
|
|
|
|
compileInfo = "<span style=\"font-weight: normal\">Last build: " +
|
|
|
|
timeSinceCompile.substr(0, 1).toUpperCase() + timeSinceCompile.substr(1) + " ago";
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
if (window.compileMessage !== "") {
|
|
|
|
compileInfo += " - " + window.compileMessage;
|
2016-11-28 11:42:58 +01:00
|
|
|
}
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
compileInfo += "</span>";
|
|
|
|
document.getElementById("notice").innerHTML = compileInfo;
|
2016-11-28 11:42:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pops up a message to the user and writes it to the console log.
|
|
|
|
*
|
|
|
|
* @param {string} str - The message to display (HTML supported)
|
|
|
|
* @param {string} style - The colour of the popup
|
|
|
|
* "danger" = red
|
|
|
|
* "warning" = amber
|
|
|
|
* "info" = blue
|
|
|
|
* "success" = green
|
|
|
|
* @param {number} timeout - The number of milliseconds before the popup closes automatically
|
|
|
|
* 0 for never (until the user closes it)
|
|
|
|
* @param {boolean} [silent=false] - Don't show the message in the popup, only print it to the
|
|
|
|
* console
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* // Pops up a red box with the message "[current time] Error: Something has gone wrong!"
|
|
|
|
* // that will need to be dismissed by the user.
|
|
|
|
* this.alert("Error: Something has gone wrong!", "danger", 0);
|
|
|
|
*
|
|
|
|
* // Pops up a blue information box with the message "[current time] Happy Christmas!"
|
|
|
|
* // that will disappear after 5 seconds.
|
|
|
|
* this.alert("Happy Christmas!", "info", 5000);
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.alert = function(str, style, timeout, silent) {
|
2016-11-28 11:42:58 +01:00
|
|
|
var time = new Date();
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
console.log("[" + time.toLocaleString() + "] " + str);
|
|
|
|
if (silent) return;
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
style = style || "danger";
|
|
|
|
timeout = timeout || 0;
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
var alertEl = document.getElementById("alert"),
|
|
|
|
alertContent = document.getElementById("alert-content");
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
alertEl.classList.remove("alert-danger");
|
|
|
|
alertEl.classList.remove("alert-warning");
|
|
|
|
alertEl.classList.remove("alert-info");
|
|
|
|
alertEl.classList.remove("alert-success");
|
|
|
|
alertEl.classList.add("alert-" + style);
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
// If the box hasn't been closed, append to it rather than replacing
|
2017-01-31 19:24:56 +01:00
|
|
|
if (alertEl.style.display === "block") {
|
|
|
|
alertContent.innerHTML +=
|
2016-11-28 11:42:58 +01:00
|
|
|
"<br><br>[" + time.toLocaleTimeString() + "] " + str;
|
|
|
|
} else {
|
2017-01-31 19:24:56 +01:00
|
|
|
alertContent.innerHTML =
|
2016-11-28 11:42:58 +01:00
|
|
|
"[" + time.toLocaleTimeString() + "] " + str;
|
|
|
|
}
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
// Stop the animation if it is in progress
|
|
|
|
$("#alert").stop();
|
2017-01-31 19:24:56 +01:00
|
|
|
alertEl.style.display = "block";
|
|
|
|
alertEl.style.opacity = 1;
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
if (timeout > 0) {
|
2017-01-31 19:24:56 +01:00
|
|
|
clearTimeout(this.alertTimeout);
|
|
|
|
this.alertTimeout = setTimeout(function(){
|
2016-11-28 11:42:58 +01:00
|
|
|
$("#alert").slideUp(100);
|
|
|
|
}, timeout);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pops up a box asking the user a question and sending the answer to a specified callback function.
|
|
|
|
*
|
|
|
|
* @param {string} title - The title of the box
|
|
|
|
* @param {string} body - The question (HTML supported)
|
|
|
|
* @param {function} callback - A function accepting one boolean argument which handles the
|
|
|
|
* response e.g. function(answer) {...}
|
|
|
|
* @param {Object} [scope=this] - The object to bind to the callback function
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* // Pops up a box asking if the user would like a cookie. Prints the answer to the console.
|
|
|
|
* this.confirm("Question", "Would you like a cookie?", function(answer) {console.log(answer);});
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.confirm = function(title, body, callback, scope) {
|
2016-11-28 11:42:58 +01:00
|
|
|
scope = scope || this;
|
|
|
|
document.getElementById("confirm-title").innerHTML = title;
|
|
|
|
document.getElementById("confirm-body").innerHTML = body;
|
|
|
|
document.getElementById("confirm-modal").style.display = "block";
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2017-01-31 19:24:56 +01:00
|
|
|
this.confirmClosed = false;
|
2016-11-28 11:42:58 +01:00
|
|
|
$("#confirm-modal").modal()
|
|
|
|
.one("show.bs.modal", function(e) {
|
2017-01-31 19:24:56 +01:00
|
|
|
this.confirmClosed = false;
|
2016-11-28 11:42:58 +01:00
|
|
|
}.bind(this))
|
|
|
|
.one("click", "#confirm-yes", function() {
|
2017-01-31 19:24:56 +01:00
|
|
|
this.confirmClosed = true;
|
2016-11-28 11:42:58 +01:00
|
|
|
callback.bind(scope)(true);
|
|
|
|
$("#confirm-modal").modal("hide");
|
|
|
|
}.bind(this))
|
|
|
|
.one("hide.bs.modal", function(e) {
|
2017-01-31 19:24:56 +01:00
|
|
|
if (!this.confirmClosed)
|
2016-11-28 11:42:58 +01:00
|
|
|
callback.bind(scope)(false);
|
2017-01-31 19:24:56 +01:00
|
|
|
this.confirmClosed = true;
|
2016-11-28 11:42:58 +01:00
|
|
|
}.bind(this));
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for the alert close button click event.
|
|
|
|
* Closes the alert box.
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.alertCloseClick = function() {
|
2016-11-28 11:42:58 +01:00
|
|
|
document.getElementById("alert").style.display = "none";
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for CyerChef statechange events.
|
|
|
|
* Fires whenever the input or recipe changes in any way.
|
|
|
|
*
|
|
|
|
* @listens Manager#statechange
|
|
|
|
* @param {event} e
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.stateChange = function(e) {
|
2017-01-31 19:24:56 +01:00
|
|
|
this.autoBake();
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
// Update the current history state (not creating a new one)
|
2017-01-31 19:24:56 +01:00
|
|
|
if (this.options.updateUrl) {
|
|
|
|
this.lastStateUrl = this.manager.controls.generateStateUrl(true, true);
|
|
|
|
window.history.replaceState({}, "CyberChef", this.lastStateUrl);
|
2016-11-28 11:42:58 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for the history popstate event.
|
|
|
|
* Reloads parameters from the URL.
|
|
|
|
*
|
|
|
|
* @param {event} e
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.popState = function(e) {
|
2017-01-31 19:24:56 +01:00
|
|
|
if (window.location.href.split("#")[0] !== this.lastStateUrl) {
|
|
|
|
this.loadURIParams();
|
2016-11-28 11:42:58 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to call an external API from this view.
|
|
|
|
*/
|
2017-03-23 18:52:20 +01:00
|
|
|
App.prototype.callApi = function(url, type, data, dataType, contentType) {
|
2016-11-28 11:42:58 +01:00
|
|
|
type = type || "POST";
|
|
|
|
data = data || {};
|
2017-01-31 19:24:56 +01:00
|
|
|
dataType = dataType || undefined;
|
|
|
|
contentType = contentType || "application/json";
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
var response = null,
|
|
|
|
success = false;
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
$.ajax({
|
|
|
|
url: url,
|
|
|
|
async: false,
|
|
|
|
type: type,
|
|
|
|
data: data,
|
2017-01-31 19:24:56 +01:00
|
|
|
dataType: dataType,
|
|
|
|
contentType: contentType,
|
2016-11-28 11:42:58 +01:00
|
|
|
success: function(data) {
|
|
|
|
success = true;
|
|
|
|
response = data;
|
|
|
|
},
|
|
|
|
error: function(data) {
|
|
|
|
success = false;
|
|
|
|
response = data;
|
|
|
|
},
|
|
|
|
});
|
2016-12-14 17:39:17 +01:00
|
|
|
|
2016-11-28 11:42:58 +01:00
|
|
|
return {
|
|
|
|
success: success,
|
|
|
|
response: response
|
|
|
|
};
|
|
|
|
};
|
2017-03-23 18:52:20 +01:00
|
|
|
|
|
|
|
export default App;
|