Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d87b14af13 | ||
|
|
83623d23cf | ||
|
|
07fba53b73 | ||
|
|
0e72d78731 | ||
|
|
768609e357 | ||
|
|
526a157421 | ||
|
|
62154309fb | ||
|
|
ad74e6c475 | ||
|
|
82d28242cc | ||
|
|
60fddf837d | ||
|
|
ee25df0c28 | ||
|
|
478af40359 | ||
|
|
71796e3dbf | ||
|
|
c43b67ea90 | ||
|
|
244421b69e | ||
|
|
1adedff61a | ||
|
|
6abd10f9e2 | ||
|
|
a85096ea11 | ||
|
|
f67157f0ad | ||
|
|
5efe9bd91d | ||
|
|
dc7a7267c9 | ||
|
|
17188b1e38 | ||
|
|
e9c3bebfff | ||
|
|
03fc22d3da | ||
|
|
5d52c49c31 | ||
|
|
d53da4cfb5 | ||
|
|
76204f5f47 | ||
|
|
b68adbd9a8 | ||
|
|
951b168f3a | ||
|
|
53d89af459 | ||
|
|
4f844ea837 | ||
|
|
b010fd88e8 | ||
|
|
66a93b81c6 | ||
|
|
274e1139fa | ||
|
|
6122e33f4f | ||
|
|
b365ce3195 | ||
|
|
0a3233d289 | ||
|
|
15aea9e9ea | ||
|
|
1b8a25ec88 | ||
|
|
50784f2600 | ||
|
|
21c0fed833 | ||
|
|
a13e2468db | ||
|
|
3fb660d816 | ||
|
|
9f60dc3dd6 | ||
|
|
02f855ff09 | ||
|
|
c39622ed1e |
No files matched your search
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"ecmaVersion": 8,
|
||||
"ecmaFeatures": {
|
||||
"impliedStrict": true
|
||||
},
|
||||
|
||||
+2
-1
@@ -220,7 +220,8 @@ module.exports = function (grunt) {
|
||||
]
|
||||
},
|
||||
stats: {
|
||||
children: false
|
||||
children: false,
|
||||
warningsFilter: /source-map/
|
||||
}
|
||||
},
|
||||
webDev: {
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
"tags": {
|
||||
"allowUnknownTags": true
|
||||
},
|
||||
"plugins": ["plugins/markdown"],
|
||||
"plugins": [
|
||||
"plugins/markdown",
|
||||
"node_modules/jsdoc-babel"
|
||||
],
|
||||
"templates": {
|
||||
"systemName": "CyberChef",
|
||||
"footer": "",
|
||||
|
||||
+4
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "5.3.1",
|
||||
"version": "5.4.1",
|
||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||
"author": "n1474335 <n1474335@gmail.com>",
|
||||
"homepage": "https://gchq.github.io/CyberChef",
|
||||
@@ -51,6 +51,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",
|
||||
@@ -68,10 +69,11 @@
|
||||
"escodegen": "^1.8.1",
|
||||
"esmangle": "^1.0.1",
|
||||
"esprima": "^3.1.3",
|
||||
"exif-parser": "^0.1.9",
|
||||
"google-code-prettify": "^1.0.5",
|
||||
"jquery": "^3.1.1",
|
||||
"jsbn": "^1.1.0",
|
||||
"jsrsasign": "^7.1.0",
|
||||
"jsrsasign": "7.1.3",
|
||||
"lodash": "^4.17.4",
|
||||
"moment": "^2.17.1",
|
||||
"moment-timezone": "^0.5.11",
|
||||
|
||||
+2
-2
@@ -34,7 +34,7 @@ const 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) {
|
||||
let 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.
|
||||
|
||||
@@ -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) {
|
||||
let opList = state.opList,
|
||||
inputType = opList[state.progress].inputType,
|
||||
outputType = opList[state.progress].outputType,
|
||||
@@ -74,7 +74,7 @@ const FlowControl = {
|
||||
for (i = 0; i < inputs.length; i++) {
|
||||
const dish = new Dish(inputs[i], inputType);
|
||||
try {
|
||||
progress = recipe.execute(dish, 0);
|
||||
progress = await recipe.execute(dish, 0);
|
||||
} catch (err) {
|
||||
if (!ignoreErrors) {
|
||||
throw err;
|
||||
|
||||
+3
-3
@@ -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;
|
||||
let 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) {
|
||||
|
||||
@@ -207,6 +207,7 @@ const Categories = [
|
||||
"Regular expression",
|
||||
"XPath expression",
|
||||
"CSS selector",
|
||||
"Extract EXIF",
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ import Endian from "../operations/Endian.js";
|
||||
import Entropy from "../operations/Entropy.js";
|
||||
import Extract from "../operations/Extract.js";
|
||||
import FileType from "../operations/FileType.js";
|
||||
import Image from "../operations/Image.js";
|
||||
import Hash from "../operations/Hash.js";
|
||||
import Hexdump from "../operations/Hexdump.js";
|
||||
import HTML from "../operations/HTML.js";
|
||||
@@ -1401,6 +1402,11 @@ const OperationConfig = {
|
||||
type: "number",
|
||||
value: Cipher.KDF_ITERATIONS
|
||||
},
|
||||
{
|
||||
name: "Hashing function",
|
||||
type: "option",
|
||||
value: Cipher.HASHERS
|
||||
},
|
||||
{
|
||||
name: "Salt (hex)",
|
||||
type: "string",
|
||||
@@ -1434,6 +1440,11 @@ const OperationConfig = {
|
||||
type: "number",
|
||||
value: Cipher.KDF_ITERATIONS
|
||||
},
|
||||
{
|
||||
name: "Hashing function",
|
||||
type: "option",
|
||||
value: Cipher.HASHERS
|
||||
},
|
||||
{
|
||||
name: "Salt (hex)",
|
||||
type: "string",
|
||||
@@ -2213,7 +2224,7 @@ const OperationConfig = {
|
||||
]
|
||||
},
|
||||
"To UNIX Timestamp": {
|
||||
description: "Parses a datetime string and returns the corresponding UNIX timestamp.<br><br>e.g. <code>Mon 1 January 2001 11:00:00 UTC</code> becomes <code>978346800</code>",
|
||||
description: "Parses a datetime string in UTC and returns the corresponding UNIX timestamp.<br><br>e.g. <code>Mon 1 January 2001 11:00:00</code> becomes <code>978346800</code>",
|
||||
run: DateTime.runToUnixTimestamp,
|
||||
inputType: "string",
|
||||
outputType: "number",
|
||||
@@ -2222,6 +2233,11 @@ const OperationConfig = {
|
||||
name: "Units",
|
||||
type: "option",
|
||||
value: DateTime.UNITS
|
||||
},
|
||||
{
|
||||
name: "Treat as UTC",
|
||||
type: "boolean",
|
||||
value: DateTime.TREAT_AS_UTC
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -3326,6 +3342,19 @@ const OperationConfig = {
|
||||
},
|
||||
]
|
||||
},
|
||||
"Extract EXIF": {
|
||||
description: [
|
||||
"Extracts EXIF data from an image.",
|
||||
"<br><br>",
|
||||
"EXIF data is metadata embedded in images (JPEG, JPG, TIFF) and audio files.",
|
||||
"<br><br>",
|
||||
"EXIF data from photos usually contains information about the image file itself as well as the device used to create it.",
|
||||
].join("\n"),
|
||||
run: Image.runEXIF,
|
||||
inputType: "byteArray",
|
||||
outputType: "string",
|
||||
args: [],
|
||||
},
|
||||
};
|
||||
|
||||
export default OperationConfig;
|
||||
@@ -309,6 +309,11 @@ const Cipher = {
|
||||
* @default
|
||||
*/
|
||||
KDF_ITERATIONS: 1,
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
HASHERS: ["MD5", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "RIPEMD160"],
|
||||
|
||||
/**
|
||||
* Derive PBKDF2 key operation.
|
||||
@@ -320,11 +325,16 @@ const Cipher = {
|
||||
runPbkdf2: function (input, args) {
|
||||
let keySize = args[0] / 32,
|
||||
iterations = args[1],
|
||||
salt = CryptoJS.enc.Hex.parse(args[2] || ""),
|
||||
inputFormat = args[3],
|
||||
outputFormat = args[4],
|
||||
hasher = args[2],
|
||||
salt = CryptoJS.enc.Hex.parse(args[3] || ""),
|
||||
inputFormat = args[4],
|
||||
outputFormat = args[5],
|
||||
passphrase = Utils.format[inputFormat].parse(input),
|
||||
key = CryptoJS.PBKDF2(passphrase, salt, { keySize: keySize, iterations: iterations });
|
||||
key = CryptoJS.PBKDF2(passphrase, salt, {
|
||||
keySize: keySize,
|
||||
hasher: CryptoJS.algo[hasher],
|
||||
iterations: iterations,
|
||||
});
|
||||
|
||||
return key.toString(Utils.format[outputFormat]);
|
||||
},
|
||||
@@ -340,11 +350,16 @@ const Cipher = {
|
||||
runEvpkdf: function (input, args) {
|
||||
let keySize = args[0] / 32,
|
||||
iterations = args[1],
|
||||
salt = CryptoJS.enc.Hex.parse(args[2] || ""),
|
||||
inputFormat = args[3],
|
||||
outputFormat = args[4],
|
||||
hasher = args[2],
|
||||
salt = CryptoJS.enc.Hex.parse(args[3] || ""),
|
||||
inputFormat = args[4],
|
||||
outputFormat = args[5],
|
||||
passphrase = Utils.format[inputFormat].parse(input),
|
||||
key = CryptoJS.EvpKDF(passphrase, salt, { keySize: keySize, iterations: iterations });
|
||||
key = CryptoJS.EvpKDF(passphrase, salt, {
|
||||
keySize: keySize,
|
||||
hasher: CryptoJS.algo[hasher],
|
||||
iterations: iterations,
|
||||
});
|
||||
|
||||
return key.toString(Utils.format[outputFormat]);
|
||||
},
|
||||
|
||||
@@ -426,7 +426,7 @@ const Code = {
|
||||
* @returns {string}
|
||||
*/
|
||||
_replaceVariableNames(input, replacer) {
|
||||
let tokenRegex = /\\"|"(?:\\"|[^"])*"|(\b[a-z0-9\-_]+\b)/ig;
|
||||
const tokenRegex = /\\"|"(?:\\"|[^"])*"|(\b[a-z0-9\-_]+\b)/ig;
|
||||
|
||||
return input.replace(tokenRegex, (...args) => {
|
||||
let match = args[0],
|
||||
@@ -450,7 +450,7 @@ const Code = {
|
||||
*
|
||||
*/
|
||||
runToSnakeCase(input, args) {
|
||||
let smart = args[0];
|
||||
const smart = args[0];
|
||||
|
||||
if (smart) {
|
||||
return Code._replaceVariableNames(input, snakeCase);
|
||||
@@ -469,7 +469,7 @@ const Code = {
|
||||
*
|
||||
*/
|
||||
runToCamelCase(input, args) {
|
||||
let smart = args[0];
|
||||
const smart = args[0];
|
||||
|
||||
if (smart) {
|
||||
return Code._replaceVariableNames(input, camelCase);
|
||||
@@ -488,7 +488,7 @@ const Code = {
|
||||
*
|
||||
*/
|
||||
runToKebabCase(input, args) {
|
||||
let smart = args[0];
|
||||
const smart = args[0];
|
||||
|
||||
if (smart) {
|
||||
return Code._replaceVariableNames(input, kebabCase);
|
||||
|
||||
@@ -46,6 +46,12 @@ const DateTime = {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
TREAT_AS_UTC: true,
|
||||
|
||||
/**
|
||||
* To UNIX Timestamp operation.
|
||||
*
|
||||
@@ -55,7 +61,8 @@ const DateTime = {
|
||||
*/
|
||||
runToUnixTimestamp: function(input, args) {
|
||||
let units = args[0],
|
||||
d = moment(input);
|
||||
treatAsUTC = args[1],
|
||||
d = treatAsUTC ? moment.utc(input) : moment(input);
|
||||
|
||||
if (units === "Seconds (s)") {
|
||||
return d.unix();
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import * as ExifParser from "exif-parser";
|
||||
import Utils from "../Utils.js";
|
||||
|
||||
|
||||
/**
|
||||
* Image operations.
|
||||
*
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
const Image = {
|
||||
|
||||
/**
|
||||
* Extract EXIF operation.
|
||||
*
|
||||
* Extracts EXIF data from a byteArray, representing a JPG or a TIFF image.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runEXIF(input, args) {
|
||||
try {
|
||||
const bytes = Uint8Array.from(input);
|
||||
const parser = ExifParser.create(bytes.buffer);
|
||||
const result = parser.parse();
|
||||
|
||||
let lines = [];
|
||||
for (let tagName in result.tags) {
|
||||
let value = result.tags[tagName];
|
||||
lines.push(`${tagName}: ${value}`);
|
||||
}
|
||||
|
||||
const numTags = lines.length;
|
||||
lines.unshift(`Found ${numTags} tags.\n`);
|
||||
return lines.join("\n");
|
||||
} catch (err) {
|
||||
throw "Could not extract EXIF data from image: " + err;
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default Image;
|
||||
@@ -124,10 +124,17 @@ const PublicKey = {
|
||||
}
|
||||
|
||||
// Signature fields
|
||||
if (r.ASN1HEX.dump(certSig).indexOf("SEQUENCE") === 0) { // DSA or ECDSA
|
||||
let breakoutSig = false;
|
||||
try {
|
||||
breakoutSig = r.ASN1HEX.dump(certSig).indexOf("SEQUENCE") === 0;
|
||||
} catch (err) {
|
||||
// Error processing signature, output without further breakout
|
||||
}
|
||||
|
||||
if (breakoutSig) { // DSA or ECDSA
|
||||
sigStr = " r: " + PublicKey._formatByteStr(r.ASN1HEX.getDecendantHexVByNthList(certSig, 0, [0]), 16, 18) + "\n" +
|
||||
" s: " + PublicKey._formatByteStr(r.ASN1HEX.getDecendantHexVByNthList(certSig, 0, [1]), 16, 18) + "\n";
|
||||
} else { // RSA
|
||||
} else { // RSA or unknown
|
||||
sigStr = " Signature: " + PublicKey._formatByteStr(certSig, 16, 18) + "\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -471,7 +471,7 @@ const StrUtils = {
|
||||
number = args[1];
|
||||
|
||||
delimiter = Utils.charRep[delimiter];
|
||||
let splitInput = input.split(delimiter);
|
||||
const splitInput = input.split(delimiter);
|
||||
|
||||
return splitInput
|
||||
.filter((line, lineIndex) => {
|
||||
@@ -499,7 +499,7 @@ const StrUtils = {
|
||||
number = args[1];
|
||||
|
||||
delimiter = Utils.charRep[delimiter];
|
||||
let splitInput = input.split(delimiter);
|
||||
const splitInput = input.split(delimiter);
|
||||
|
||||
return splitInput
|
||||
.filter((line, lineIndex) => {
|
||||
|
||||
+37
-4
@@ -30,6 +30,7 @@ const 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,19 +68,49 @@ 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;
|
||||
|
||||
let inputLoadingIcon = document.querySelector("#input .title .loading-icon"),
|
||||
outputLoadingIcon = document.querySelector("#output .title .loading-icon"),
|
||||
outputElement = document.querySelector("#output-text");
|
||||
|
||||
if (bakingStatus) {
|
||||
inputLoadingIcon.style.display = "inline-block";
|
||||
outputLoadingIcon.style.display = "inline-block";
|
||||
outputElement.classList.add("disabled");
|
||||
outputElement.disabled = true;
|
||||
} else {
|
||||
inputLoadingIcon.style.display = "none";
|
||||
outputLoadingIcon.style.display = "none";
|
||||
outputElement.classList.remove("disabled");
|
||||
outputElement.disabled = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
App.prototype.bake = function(step) {
|
||||
App.prototype.bake = async function(step) {
|
||||
let response;
|
||||
|
||||
if (this.baking) return;
|
||||
|
||||
this.setBakingStatus(true);
|
||||
|
||||
try {
|
||||
response = this.chef.bake(
|
||||
this.getInput(), // The user's input
|
||||
this.getRecipeConfig(), // The configuration of the recipe
|
||||
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
|
||||
this.progress, // The current position in the recipe
|
||||
step // Whether or not to take one step or execute the whole recipe
|
||||
@@ -88,6 +119,8 @@ App.prototype.bake = function(step) {
|
||||
this.handleError(err);
|
||||
}
|
||||
|
||||
this.setBakingStatus(false);
|
||||
|
||||
if (!response) return;
|
||||
|
||||
if (response.error) {
|
||||
|
||||
+44
-44
@@ -23,14 +23,14 @@ const ControlsWaiter = function(app, manager) {
|
||||
* without wrapping or overflowing.
|
||||
*/
|
||||
ControlsWaiter.prototype.adjustWidth = function() {
|
||||
let controls = document.getElementById("controls"),
|
||||
step = document.getElementById("step"),
|
||||
clrBreaks = document.getElementById("clr-breaks"),
|
||||
saveImg = document.querySelector("#save img"),
|
||||
loadImg = document.querySelector("#load img"),
|
||||
stepImg = document.querySelector("#step img"),
|
||||
clrRecipImg = document.querySelector("#clr-recipe img"),
|
||||
clrBreaksImg = document.querySelector("#clr-breaks img");
|
||||
const controls = document.getElementById("controls");
|
||||
const step = document.getElementById("step");
|
||||
const clrBreaks = document.getElementById("clr-breaks");
|
||||
const saveImg = document.querySelector("#save img");
|
||||
const loadImg = document.querySelector("#load img");
|
||||
const stepImg = document.querySelector("#step img");
|
||||
const clrRecipImg = document.querySelector("#clr-recipe img");
|
||||
const clrBreaksImg = document.querySelector("#clr-breaks img");
|
||||
|
||||
if (controls.clientWidth < 470) {
|
||||
step.childNodes[1].nodeValue = " Step";
|
||||
@@ -100,8 +100,8 @@ ControlsWaiter.prototype.stepClick = function() {
|
||||
* Handler for changes made to the Auto Bake checkbox.
|
||||
*/
|
||||
ControlsWaiter.prototype.autoBakeChange = function() {
|
||||
let autoBakeLabel = document.getElementById("auto-bake-label"),
|
||||
autoBakeCheckbox = document.getElementById("auto-bake");
|
||||
const autoBakeLabel = document.getElementById("auto-bake-label");
|
||||
const autoBakeCheckbox = document.getElementById("auto-bake");
|
||||
|
||||
this.app.autoBake_ = autoBakeCheckbox.checked;
|
||||
|
||||
@@ -145,10 +145,10 @@ ControlsWaiter.prototype.clearBreaksClick = function() {
|
||||
ControlsWaiter.prototype.initialiseSaveLink = function(recipeConfig) {
|
||||
recipeConfig = recipeConfig || this.app.getRecipeConfig();
|
||||
|
||||
let includeRecipe = document.getElementById("save-link-recipe-checkbox").checked,
|
||||
includeInput = document.getElementById("save-link-input-checkbox").checked,
|
||||
saveLinkEl = document.getElementById("save-link"),
|
||||
saveLink = this.generateStateUrl(includeRecipe, includeInput, recipeConfig);
|
||||
const includeRecipe = document.getElementById("save-link-recipe-checkbox").checked;
|
||||
const includeInput = document.getElementById("save-link-input-checkbox").checked;
|
||||
const saveLinkEl = document.getElementById("save-link");
|
||||
const saveLink = this.generateStateUrl(includeRecipe, includeInput, recipeConfig);
|
||||
|
||||
saveLinkEl.innerHTML = Utils.truncate(saveLink, 120);
|
||||
saveLinkEl.setAttribute("href", saveLink);
|
||||
@@ -167,23 +167,27 @@ ControlsWaiter.prototype.initialiseSaveLink = function(recipeConfig) {
|
||||
ControlsWaiter.prototype.generateStateUrl = function(includeRecipe, includeInput, recipeConfig, baseURL) {
|
||||
recipeConfig = recipeConfig || this.app.getRecipeConfig();
|
||||
|
||||
let link = baseURL || window.location.protocol + "//" +
|
||||
window.location.host +
|
||||
window.location.pathname,
|
||||
recipeStr = JSON.stringify(recipeConfig),
|
||||
inputStr = Utils.toBase64(this.app.getInput(), "A-Za-z0-9+/"); // B64 alphabet with no padding
|
||||
const link = baseURL || window.location.protocol + "//" +
|
||||
window.location.host +
|
||||
window.location.pathname;
|
||||
const recipeStr = JSON.stringify(recipeConfig);
|
||||
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);
|
||||
|
||||
if (includeRecipe) {
|
||||
link += "?recipe=" + encodeURIComponent(recipeStr);
|
||||
}
|
||||
const params = [
|
||||
includeRecipe ? ["recipe", recipeStr] : undefined,
|
||||
includeInput ? ["input", inputStr] : undefined,
|
||||
];
|
||||
|
||||
if (includeRecipe && includeInput) {
|
||||
link += "&input=" + encodeURIComponent(inputStr);
|
||||
} else if (includeInput) {
|
||||
link += "?input=" + encodeURIComponent(inputStr);
|
||||
const query = params
|
||||
.filter(v => v)
|
||||
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
||||
.join("&");
|
||||
|
||||
if (query) {
|
||||
return `${link}?${query}`;
|
||||
}
|
||||
|
||||
return link;
|
||||
@@ -205,8 +209,8 @@ ControlsWaiter.prototype.saveTextChange = function() {
|
||||
* Handler for the 'Save' command. Pops up the save dialog box.
|
||||
*/
|
||||
ControlsWaiter.prototype.saveClick = function() {
|
||||
let recipeConfig = this.app.getRecipeConfig(),
|
||||
recipeStr = JSON.stringify(recipeConfig).replace(/},{/g, "},\n{");
|
||||
const recipeConfig = this.app.getRecipeConfig();
|
||||
const recipeStr = JSON.stringify(recipeConfig).replace(/},{/g, "},\n{");
|
||||
|
||||
document.getElementById("save-text").value = recipeStr;
|
||||
|
||||
@@ -244,8 +248,8 @@ ControlsWaiter.prototype.loadClick = function() {
|
||||
* Saves the recipe specified in the save textarea to local storage.
|
||||
*/
|
||||
ControlsWaiter.prototype.saveButtonClick = function() {
|
||||
let recipeName = Utils.escapeHtml(document.getElementById("save-name").value);
|
||||
let recipeStr = document.getElementById("save-text").value;
|
||||
const recipeName = Utils.escapeHtml(document.getElementById("save-name").value);
|
||||
const recipeStr = document.getElementById("save-text").value;
|
||||
|
||||
if (!recipeName) {
|
||||
this.app.alert("Please enter a recipe name", "danger", 2000);
|
||||
@@ -303,13 +307,11 @@ ControlsWaiter.prototype.populateLoadRecipesList = function() {
|
||||
* Removes the currently selected recipe from local storage.
|
||||
*/
|
||||
ControlsWaiter.prototype.loadDeleteClick = function() {
|
||||
let id = parseInt(document.getElementById("load-name").value, 10),
|
||||
savedRecipes = localStorage.savedRecipes ?
|
||||
const id = parseInt(document.getElementById("load-name").value, 10);
|
||||
const rawSavedRecipes = localStorage.savedRecipes ?
|
||||
JSON.parse(localStorage.savedRecipes) : [];
|
||||
|
||||
savedRecipes = savedRecipes.filter(function(r) {
|
||||
return r.id !== id;
|
||||
});
|
||||
const savedRecipes = rawSavedRecipes.filter(r => r.id !== id);
|
||||
|
||||
localStorage.savedRecipes = JSON.stringify(savedRecipes);
|
||||
this.populateLoadRecipesList();
|
||||
@@ -320,14 +322,12 @@ ControlsWaiter.prototype.loadDeleteClick = function() {
|
||||
* Displays the selected recipe in the load text box.
|
||||
*/
|
||||
ControlsWaiter.prototype.loadNameChange = function(e) {
|
||||
let el = e.target,
|
||||
savedRecipes = localStorage.savedRecipes ?
|
||||
JSON.parse(localStorage.savedRecipes) : [],
|
||||
id = parseInt(el.value, 10);
|
||||
const el = e.target;
|
||||
const savedRecipes = localStorage.savedRecipes ?
|
||||
JSON.parse(localStorage.savedRecipes) : [];
|
||||
const id = parseInt(el.value, 10);
|
||||
|
||||
const recipe = savedRecipes.filter(function(r) {
|
||||
return r.id === id;
|
||||
})[0];
|
||||
const recipe = savedRecipes.find(r => r.id === id);
|
||||
|
||||
document.getElementById("load-text").value = recipe.recipe;
|
||||
};
|
||||
@@ -352,8 +352,8 @@ ControlsWaiter.prototype.loadButtonClick = function() {
|
||||
* Populates the bug report information box with useful technical info.
|
||||
*/
|
||||
ControlsWaiter.prototype.supportButtonClick = function() {
|
||||
let reportBugInfo = document.getElementById("report-bug-info"),
|
||||
saveLink = this.generateStateUrl(true, true, null, "https://gchq.github.io/CyberChef/");
|
||||
const reportBugInfo = document.getElementById("report-bug-info");
|
||||
const saveLink = this.generateStateUrl(true, true, null, "https://gchq.github.io/CyberChef/");
|
||||
|
||||
reportBugInfo.innerHTML = "* CyberChef compile time: " + COMPILE_TIME + "\n" +
|
||||
"* User-Agent: \n" + navigator.userAgent + "\n" +
|
||||
|
||||
@@ -158,13 +158,12 @@ HTMLIngredient.prototype.toHtml = function() {
|
||||
* @param {event} e
|
||||
*/
|
||||
HTMLIngredient.prototype.toggleDisableArgs = function(e) {
|
||||
let el = e.target,
|
||||
op = el.parentNode.parentNode,
|
||||
args = op.querySelectorAll(".arg-group"),
|
||||
els;
|
||||
const el = e.target;
|
||||
const op = el.parentNode.parentNode;
|
||||
const args = op.querySelectorAll(".arg-group");
|
||||
|
||||
for (let i = 0; i < this.disableArgs.length; i++) {
|
||||
els = args[this.disableArgs[i]].querySelectorAll("input, select, button");
|
||||
const els = args[this.disableArgs[i]].querySelectorAll("input, select, button");
|
||||
|
||||
for (let j = 0; j < els.length; j++) {
|
||||
if (els[j].getAttribute("disabled")) {
|
||||
@@ -186,9 +185,9 @@ HTMLIngredient.prototype.toggleDisableArgs = function(e) {
|
||||
* @param {event} e
|
||||
*/
|
||||
HTMLIngredient.prototype.populateOptionChange = function(e) {
|
||||
let el = e.target,
|
||||
op = el.parentNode.parentNode,
|
||||
target = op.querySelectorAll(".arg-group")[this.target].querySelector("input, select, textarea");
|
||||
const el = e.target;
|
||||
const op = el.parentNode.parentNode;
|
||||
const target = op.querySelectorAll(".arg-group")[this.target].querySelector("input, select, textarea");
|
||||
|
||||
target.value = el.childNodes[el.selectedIndex].getAttribute("populate-value");
|
||||
|
||||
|
||||
@@ -64,8 +64,8 @@ HighlighterWaiter.prototype._isSelectionBackwards = function() {
|
||||
* @returns {number}
|
||||
*/
|
||||
HighlighterWaiter.prototype._getOutputHtmlOffset = function(node, offset) {
|
||||
let sel = window.getSelection(),
|
||||
range = document.createRange();
|
||||
const sel = window.getSelection();
|
||||
const range = document.createRange();
|
||||
|
||||
range.selectNodeContents(document.getElementById("output-html"));
|
||||
range.setEnd(node, offset);
|
||||
@@ -85,8 +85,8 @@ HighlighterWaiter.prototype._getOutputHtmlOffset = function(node, offset) {
|
||||
* @returns {number} pos.end
|
||||
*/
|
||||
HighlighterWaiter.prototype._getOutputHtmlSelectionOffsets = function() {
|
||||
let sel = window.getSelection(),
|
||||
range,
|
||||
const sel = window.getSelection();
|
||||
let range,
|
||||
start = 0,
|
||||
end = 0,
|
||||
backwards = false;
|
||||
@@ -151,9 +151,9 @@ HighlighterWaiter.prototype.inputMousedown = function(e) {
|
||||
this.mouseTarget = HighlighterWaiter.INPUT;
|
||||
this.removeHighlights();
|
||||
|
||||
let el = e.target,
|
||||
start = el.selectionStart,
|
||||
end = el.selectionEnd;
|
||||
const el = e.target;
|
||||
const start = el.selectionStart;
|
||||
const end = el.selectionEnd;
|
||||
|
||||
if (start !== 0 || end !== 0) {
|
||||
document.getElementById("input-selection-info").innerHTML = this.selectionInfo(start, end);
|
||||
@@ -173,9 +173,9 @@ HighlighterWaiter.prototype.outputMousedown = function(e) {
|
||||
this.mouseTarget = HighlighterWaiter.OUTPUT;
|
||||
this.removeHighlights();
|
||||
|
||||
let el = e.target,
|
||||
start = el.selectionStart,
|
||||
end = el.selectionEnd;
|
||||
const el = e.target;
|
||||
const start = el.selectionStart;
|
||||
const end = el.selectionEnd;
|
||||
|
||||
if (start !== 0 || end !== 0) {
|
||||
document.getElementById("output-selection-info").innerHTML = this.selectionInfo(start, end);
|
||||
@@ -244,9 +244,9 @@ HighlighterWaiter.prototype.inputMousemove = function(e) {
|
||||
this.mouseTarget !== HighlighterWaiter.INPUT)
|
||||
return;
|
||||
|
||||
let el = e.target,
|
||||
start = el.selectionStart,
|
||||
end = el.selectionEnd;
|
||||
const el = e.target;
|
||||
const start = el.selectionStart;
|
||||
const end = el.selectionEnd;
|
||||
|
||||
if (start !== 0 || end !== 0) {
|
||||
document.getElementById("input-selection-info").innerHTML = this.selectionInfo(start, end);
|
||||
@@ -268,9 +268,9 @@ HighlighterWaiter.prototype.outputMousemove = function(e) {
|
||||
this.mouseTarget !== HighlighterWaiter.OUTPUT)
|
||||
return;
|
||||
|
||||
let el = e.target,
|
||||
start = el.selectionStart,
|
||||
end = el.selectionEnd;
|
||||
const el = e.target;
|
||||
const start = el.selectionStart;
|
||||
const end = el.selectionEnd;
|
||||
|
||||
if (start !== 0 || end !== 0) {
|
||||
document.getElementById("output-selection-info").innerHTML = this.selectionInfo(start, end);
|
||||
@@ -308,11 +308,11 @@ HighlighterWaiter.prototype.outputHtmlMousemove = function(e) {
|
||||
* @returns {string}
|
||||
*/
|
||||
HighlighterWaiter.prototype.selectionInfo = function(start, end) {
|
||||
let width = end.toString().length;
|
||||
width = width < 2 ? 2 : width;
|
||||
let startStr = Utils.pad(start.toString(), width, " ").replace(/ /g, " "),
|
||||
endStr = Utils.pad(end.toString(), width, " ").replace(/ /g, " "),
|
||||
lenStr = Utils.pad((end-start).toString(), width, " ").replace(/ /g, " ");
|
||||
const len = end.toString().length;
|
||||
const width = len < 2 ? 2 : len;
|
||||
const startStr = Utils.pad(start.toString(), width, " ").replace(/ /g, " ");
|
||||
const endStr = Utils.pad(end.toString(), width, " ").replace(/ /g, " ");
|
||||
const lenStr = Utils.pad((end-start).toString(), width, " ").replace(/ /g, " ");
|
||||
|
||||
return "start: " + startStr + "<br>end: " + endStr + "<br>length: " + lenStr;
|
||||
};
|
||||
@@ -339,8 +339,8 @@ HighlighterWaiter.prototype.removeHighlights = function() {
|
||||
* @returns {Object[]} highlights[].args
|
||||
*/
|
||||
HighlighterWaiter.prototype.generateHighlightList = function() {
|
||||
let recipeConfig = this.app.getRecipeConfig(),
|
||||
highlights = [];
|
||||
const recipeConfig = this.app.getRecipeConfig();
|
||||
const highlights = [];
|
||||
|
||||
for (let i = 0; i < recipeConfig.length; i++) {
|
||||
if (recipeConfig[i].disabled) continue;
|
||||
@@ -452,11 +452,11 @@ HighlighterWaiter.prototype.highlight = function(textarea, highlighter, pos) {
|
||||
// be displayed by the HTML textarea and will mess up highlighting offsets.
|
||||
if (!this.app.dishStr || this.app.dishStr.indexOf("\r") >= 0) return false;
|
||||
|
||||
let startPlaceholder = "[startHighlight]",
|
||||
startPlaceholderRegex = /\[startHighlight\]/g,
|
||||
endPlaceholder = "[endHighlight]",
|
||||
endPlaceholderRegex = /\[endHighlight\]/g,
|
||||
text = textarea.value;
|
||||
const startPlaceholder = "[startHighlight]";
|
||||
const startPlaceholderRegex = /\[startHighlight\]/g;
|
||||
const endPlaceholder = "[endHighlight]";
|
||||
const endPlaceholderRegex = /\[endHighlight\]/g;
|
||||
let text = textarea.value;
|
||||
|
||||
// Put placeholders in position
|
||||
// If there's only one value, select that
|
||||
|
||||
@@ -92,8 +92,8 @@ InputWaiter.prototype.inputChange = function(e) {
|
||||
this.app.progress = 0;
|
||||
|
||||
// Update the input metadata info
|
||||
let inputText = this.get(),
|
||||
lines = inputText.count("\n") + 1;
|
||||
const inputText = this.get();
|
||||
const lines = inputText.count("\n") + 1;
|
||||
|
||||
this.setInputInfo(inputText.length, lines);
|
||||
|
||||
@@ -149,13 +149,13 @@ InputWaiter.prototype.inputDrop = function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
let el = e.target,
|
||||
file = e.dataTransfer.files[0],
|
||||
text = e.dataTransfer.getData("Text"),
|
||||
reader = new FileReader(),
|
||||
inputCharcode = "",
|
||||
offset = 0,
|
||||
CHUNK_SIZE = 20480; // 20KB
|
||||
const el = e.target;
|
||||
const file = e.dataTransfer.files[0];
|
||||
const text = e.dataTransfer.getData("Text");
|
||||
const reader = new FileReader();
|
||||
let inputCharcode = "";
|
||||
let offset = 0;
|
||||
const CHUNK_SIZE = 20480; // 20KB
|
||||
|
||||
const setInput = function() {
|
||||
if (inputCharcode.length > 100000 && this.app.autoBake_) {
|
||||
|
||||
+8
-7
@@ -261,15 +261,16 @@ Manager.prototype.addDynamicListener = function(selector, eventType, callback, s
|
||||
* @param {Event} e - The event to be handled
|
||||
*/
|
||||
Manager.prototype.dynamicListenerHandler = function(e) {
|
||||
let handlers = this.dynamicHandlers[e.type],
|
||||
matches = e.target.matches ||
|
||||
e.target.webkitMatchesSelector ||
|
||||
e.target.mozMatchesSelector ||
|
||||
e.target.msMatchesSelector ||
|
||||
e.target.oMatchesSelector;
|
||||
const { type, target } = e;
|
||||
const handlers = this.dynamicHandlers[type];
|
||||
const matches = target.matches ||
|
||||
target.webkitMatchesSelector ||
|
||||
target.mozMatchesSelector ||
|
||||
target.msMatchesSelector ||
|
||||
target.oMatchesSelector;
|
||||
|
||||
for (let i = 0; i < handlers.length; i++) {
|
||||
if (matches && e.target[matches.name](handlers[i].selector)) {
|
||||
if (matches && matches.call(target, handlers[i].selector)) {
|
||||
handlers[i].callback(e);
|
||||
}
|
||||
}
|
||||
|
||||
+18
-24
@@ -68,9 +68,9 @@ OperationsWaiter.prototype.searchOperations = function(e) {
|
||||
ops[selected-1].classList.add("selected-op");
|
||||
}
|
||||
} else {
|
||||
let searchResultsEl = document.getElementById("search-results"),
|
||||
el = e.target,
|
||||
str = el.value;
|
||||
const searchResultsEl = document.getElementById("search-results");
|
||||
const el = e.target;
|
||||
const str = el.value;
|
||||
|
||||
while (searchResultsEl.firstChild) {
|
||||
try {
|
||||
@@ -81,12 +81,10 @@ OperationsWaiter.prototype.searchOperations = function(e) {
|
||||
|
||||
$("#categories .in").collapse("hide");
|
||||
if (str) {
|
||||
let matchedOps = this.filterOperations(str, true),
|
||||
matchedOpsHtml = "";
|
||||
|
||||
for (let i = 0; i < matchedOps.length; i++) {
|
||||
matchedOpsHtml += matchedOps[i].toStubHtml();
|
||||
}
|
||||
const matchedOps = this.filterOperations(str, true);
|
||||
const matchedOpsHtml = matchedOps
|
||||
.map(v => v.toStubHtml())
|
||||
.join("");
|
||||
|
||||
searchResultsEl.innerHTML = matchedOpsHtml;
|
||||
searchResultsEl.dispatchEvent(this.manager.oplistcreate);
|
||||
@@ -103,16 +101,16 @@ OperationsWaiter.prototype.searchOperations = function(e) {
|
||||
* name and description
|
||||
* @returns {string[]}
|
||||
*/
|
||||
OperationsWaiter.prototype.filterOperations = function(searchStr, highlight) {
|
||||
let matchedOps = [],
|
||||
matchedDescs = [];
|
||||
OperationsWaiter.prototype.filterOperations = function(inStr, highlight) {
|
||||
const matchedOps = [];
|
||||
const matchedDescs = [];
|
||||
|
||||
searchStr = searchStr.toLowerCase();
|
||||
const searchStr = inStr.toLowerCase();
|
||||
|
||||
for (const opName in this.app.operations) {
|
||||
let op = this.app.operations[opName],
|
||||
namePos = opName.toLowerCase().indexOf(searchStr),
|
||||
descPos = op.description.toLowerCase().indexOf(searchStr);
|
||||
const op = this.app.operations[opName];
|
||||
const namePos = opName.toLowerCase().indexOf(searchStr);
|
||||
const descPos = op.description.toLowerCase().indexOf(searchStr);
|
||||
|
||||
if (namePos >= 0 || descPos >= 0) {
|
||||
const operation = new HTMLOperation(opName, this.app.operations[opName], this.app, this.manager);
|
||||
@@ -236,12 +234,8 @@ OperationsWaiter.prototype.editFavouritesClick = function(e) {
|
||||
* Saves the selected favourites and reloads them.
|
||||
*/
|
||||
OperationsWaiter.prototype.saveFavouritesClick = function() {
|
||||
let favouritesList = [],
|
||||
favs = document.querySelectorAll("#edit-favourites-list li");
|
||||
|
||||
for (let i = 0; i < favs.length; i++) {
|
||||
favouritesList.push(favs[i].textContent);
|
||||
}
|
||||
const favs = document.querySelectorAll("#edit-favourites-list li");
|
||||
const favouritesList = Array.from(favs, e => e.textContent);
|
||||
|
||||
this.app.saveFavourites(favouritesList);
|
||||
this.app.loadFavourites();
|
||||
@@ -281,8 +275,8 @@ OperationsWaiter.prototype.opIconMouseover = function(e) {
|
||||
* @param {event} e
|
||||
*/
|
||||
OperationsWaiter.prototype.opIconMouseleave = function(e) {
|
||||
let opEl = e.target.parentNode,
|
||||
toEl = e.toElement || e.relatedElement;
|
||||
const opEl = e.target.parentNode;
|
||||
const toEl = e.toElement || e.relatedElement;
|
||||
|
||||
if (e.target.getAttribute("data-toggle") === "popover" && toEl === opEl) {
|
||||
$(opEl).popover("show");
|
||||
|
||||
@@ -75,8 +75,8 @@ OptionsWaiter.prototype.resetOptionsClick = function() {
|
||||
* @param {boolean} state
|
||||
*/
|
||||
OptionsWaiter.prototype.switchChange = function(e, state) {
|
||||
let el = e.target,
|
||||
option = el.getAttribute("option");
|
||||
const el = e.target;
|
||||
const option = el.getAttribute("option");
|
||||
|
||||
this.app.options[option] = state;
|
||||
localStorage.setItem("options", JSON.stringify(this.app.options));
|
||||
@@ -90,8 +90,8 @@ OptionsWaiter.prototype.switchChange = function(e, state) {
|
||||
* @param {event} e
|
||||
*/
|
||||
OptionsWaiter.prototype.numberChange = function(e) {
|
||||
let el = e.target,
|
||||
option = el.getAttribute("option");
|
||||
const el = e.target;
|
||||
const option = el.getAttribute("option");
|
||||
|
||||
this.app.options[option] = parseInt(el.value, 10);
|
||||
localStorage.setItem("options", JSON.stringify(this.app.options));
|
||||
@@ -105,8 +105,8 @@ OptionsWaiter.prototype.numberChange = function(e) {
|
||||
* @param {event} e
|
||||
*/
|
||||
OptionsWaiter.prototype.selectChange = function(e) {
|
||||
let el = e.target,
|
||||
option = el.getAttribute("option");
|
||||
const el = e.target;
|
||||
const option = el.getAttribute("option");
|
||||
|
||||
this.app.options[option] = el.value;
|
||||
localStorage.setItem("options", JSON.stringify(this.app.options));
|
||||
|
||||
+11
-11
@@ -36,10 +36,10 @@ OutputWaiter.prototype.get = function() {
|
||||
* @param {number} duration - The length of time (ms) it took to generate the output
|
||||
*/
|
||||
OutputWaiter.prototype.set = function(dataStr, type, duration) {
|
||||
let outputText = document.getElementById("output-text"),
|
||||
outputHtml = document.getElementById("output-html"),
|
||||
outputHighlighter = document.getElementById("output-highlighter"),
|
||||
inputHighlighter = document.getElementById("input-highlighter");
|
||||
const outputText = document.getElementById("output-text");
|
||||
const outputHtml = document.getElementById("output-html");
|
||||
const outputHighlighter = document.getElementById("output-highlighter");
|
||||
const inputHighlighter = document.getElementById("input-highlighter");
|
||||
|
||||
if (type === "html") {
|
||||
outputText.style.display = "none";
|
||||
@@ -103,11 +103,11 @@ OutputWaiter.prototype.setOutputInfo = function(length, lines, duration) {
|
||||
* without wrapping or overflowing.
|
||||
*/
|
||||
OutputWaiter.prototype.adjustWidth = function() {
|
||||
let output = document.getElementById("output"),
|
||||
saveToFile = document.getElementById("save-to-file"),
|
||||
switchIO = document.getElementById("switch"),
|
||||
undoSwitch = document.getElementById("undo-switch"),
|
||||
maximiseOutput = document.getElementById("maximise-output");
|
||||
const output = document.getElementById("output");
|
||||
const saveToFile = document.getElementById("save-to-file");
|
||||
const switchIO = document.getElementById("switch");
|
||||
const undoSwitch = document.getElementById("undo-switch");
|
||||
const maximiseOutput = document.getElementById("maximise-output");
|
||||
|
||||
if (output.clientWidth < 680) {
|
||||
saveToFile.childNodes[1].nodeValue = "";
|
||||
@@ -129,8 +129,8 @@ OutputWaiter.prototype.adjustWidth = function() {
|
||||
* Saves the current output to a file, downloaded as a URL octet stream.
|
||||
*/
|
||||
OutputWaiter.prototype.saveClick = function() {
|
||||
let data = Utils.toBase64(this.app.dishStr),
|
||||
filename = window.prompt("Please enter a filename:", "download.dat");
|
||||
const data = Utils.toBase64(this.app.dishStr);
|
||||
const filename = window.prompt("Please enter a filename:", "download.dat");
|
||||
|
||||
if (filename) {
|
||||
const el = document.createElement("a");
|
||||
|
||||
@@ -60,8 +60,8 @@ RecipeWaiter.prototype.initialiseOperationDragNDrop = function() {
|
||||
}.bind(this));
|
||||
|
||||
Sortable.utils.on(recList, "touchend", function(e) {
|
||||
let loc = e.changedTouches[0],
|
||||
target = document.elementFromPoint(loc.clientX, loc.clientY);
|
||||
const loc = e.changedTouches[0];
|
||||
const target = document.elementFromPoint(loc.clientX, loc.clientY);
|
||||
|
||||
this.removeIntent = !recList.contains(target);
|
||||
}.bind(this));
|
||||
@@ -276,8 +276,9 @@ RecipeWaiter.prototype.operationChildDblclick = function(e) {
|
||||
* @returns {recipeConfig}
|
||||
*/
|
||||
RecipeWaiter.prototype.getConfig = function() {
|
||||
let config = [], ingredients, ingList, disabled, bp, item,
|
||||
operations = document.querySelectorAll("#rec-list li.operation");
|
||||
const config = [];
|
||||
let ingredients, ingList, disabled, bp, item;
|
||||
const operations = document.querySelectorAll("#rec-list li.operation");
|
||||
|
||||
for (let i = 0; i < operations.length; i++) {
|
||||
ingredients = [];
|
||||
@@ -402,8 +403,8 @@ RecipeWaiter.prototype.clearRecipe = function() {
|
||||
* @param {event} e
|
||||
*/
|
||||
RecipeWaiter.prototype.dropdownToggleClick = function(e) {
|
||||
let el = e.target,
|
||||
button = el.parentNode.parentNode.previousSibling;
|
||||
const el = e.target;
|
||||
const button = el.parentNode.parentNode.previousSibling;
|
||||
|
||||
button.innerHTML = el.textContent + " <span class='caret'></span>";
|
||||
this.ingChange();
|
||||
|
||||
@@ -19,69 +19,12 @@ const SeasonalWaiter = function(app, manager) {
|
||||
* Loads all relevant items depending on the current date.
|
||||
*/
|
||||
SeasonalWaiter.prototype.load = function() {
|
||||
//var now = new Date();
|
||||
|
||||
// SpiderChef
|
||||
// if (now.getMonth() === 3 && now.getDate() === 1) { // Apr 1
|
||||
// this.insertSpiderIcons();
|
||||
// this.insertSpiderText();
|
||||
// }
|
||||
|
||||
// Konami code
|
||||
this.kkeys = [];
|
||||
window.addEventListener("keydown", this.konamiCodeListener.bind(this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Replaces chef icons with spider icons.
|
||||
* #spiderchef
|
||||
*/
|
||||
SeasonalWaiter.prototype.insertSpiderIcons = function() {
|
||||
let spider16 = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB3UlEQVQ4y2NgGJaAmYGBgVnf0oKJgYGBobWtXamqqoYTn2I4CI+LTzM2NTulpKbu+vPHz2dV5RWlluZmi3j5+KqFJSSEzpw8uQPdAEYYIzo5Kfjrl28rWFlZzjAzMYuEBQao3Lh+g+HGvbsMzExMDN++fWf4/PXLBzY2tqYNK1f2+4eHM2xcuRLigsT09Igf3384MTExbf767etBI319jU8fPsi+//jx/72HDxh5uLkZ7ty7y/Dz1687Avz8n2UUFR3Z2NjOySoqfmdhYGBg+PbtuwI7O8e5H79+8X379t357PnzYo+ePP7y6cuXc9++f69nYGRsvf/w4XdtLS2R799/bBUWFHr57sP7Jbs3b/ZkzswvUP3165fZ7z9//r988WIVAyPDr8tXr576+u3bpb9//7YwMjKeV1dV41NWVGoVEhDgPH761DJREeHaz1+/lqlpafUx6+jrRfz4+fPy+w8fTu/fsf3uw7t3L39+//4cv7DwGQYGhpdPbt9m4BcRFlNWVJC4fuvWASszs4C379792Ldt2xZBUdEdDP5hYSqQGIjDGa965uYKCalpZQwMDAxhMTG9DAwMDLaurhIkJY7A8IgGBgYGBgd3Dz2yUpeFo6O4rasrA9T24ZRxAAMTwMpgEJwLAAAAAElFTkSuQmCC",
|
||||
spider32 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAACYVBMVEUAAAAcJSU2Pz85QkM9RUWEhIWMjI2MkJEcJSU2Pz85QkM9RUWWlpc9RUVXXl4cJSU2Pz85QkM8REU9RUVRWFh6ens9RUVCSkpNVFRdY2McJSU5QkM7REQ9RUVGTk5KUlJQVldcY2Rla2uTk5WampscJSVUWltZX2BrcHF1e3scJSUjLCw9RUVASEhFTU1HTk9bYWJeZGRma2xudHV1eHiZmZocJSUyOjpJUFFQVldSWlpTWVpXXl5YXl5rb3B9fX6RkZIcJSUmLy8tNTU9RUVFTU1IT1BOVldRV1hTWlp0enocJSUfKChJUFBWXV1hZ2hnbGwcJSVETExLUlJLU1NNVVVPVlZYXl9cY2RiaGlobW5rcXFyd3h0eHgcJSUpMTFDS0tQV1dRV1hSWFlWXF1bYWJma2tobW5uc3SsrK0cJSVJUFBMVFROVlZVW1xZX2BdYmNhZ2hjaGhla2tqcHBscHE4Pz9KUlJRWVlSWVlXXF1aYGFbYWFfZWZlampqbW4cJSUgKSkiKysuNjY0PD01PT07QkNES0tHTk5JUFBMUlNMU1NOU1ROVVVPVVZRVlZRV1dSWVlWXFxXXV5aX2BbYWFbYWJcYmJcYmNcY2RdYmNgZmZhZmdkaWpkampkamtlamtla2tma2tma2xnbG1obW5pbG1pb3Bqb3Brb3BtcXJudHVvcHFvcXJvc3NwcXNwdXVxc3RzeXl1eXp2eXl3ent6e3x+gYKAhISBg4SKi4yLi4yWlpeampudnZ6fn6CkpaanqKiur6+vr7C4uLm6urq6u7u8vLy9vb3Av8DR0dL2b74UAAAAgHRSTlMAEBAQEBAQECAgICAgMDBAQEBAQEBAUFBQUGBgYGBgYGBgYGBgcHBwcHCAgICAgICAgICAgICPj4+Pj4+Pj4+Pj5+fn5+fn5+fn5+vr6+vr6+/v7+/v7+/v7+/v7+/z8/Pz8/Pz8/Pz8/P39/f39/f39/f39/f7+/v7+/v7+/v78x6RlYAAAGBSURBVDjLY2AYWUCSgUGAk4GBTdlUhQebvP7yjIgCPQbWzBMnjx5wwJSX37Rwfm1isqj9/iPHTuxYlyeMJi+yunfptBkZOw/uWj9h3vatcycu8eRGlldb3Vsts3ph/cFTh7fN3bCoe2Vf8+TZoQhTvBa6REozVC7cuPvQnmULJm1e2z+308eyJieEBSLPXbKQIUqQIczk+N6eNaumtnZMaWhaHM89m8XVCqJA02Y5w0xmga6yfVsamtrN4xoXNzS0JTHkK3CXy4EVFMumcxUy2LbENTVkZfEzMDAudtJyTmNwS2XQreAFyvOlK9louDNVaXurmjkGgnTMkWDgXswtNouFISEX6Awv+RihQi5OcYY4DtVARpCCFCMGhiJ1hjwFBpagEAaWEpFoC0WQOCOjFMRRwXYMDB4BDLJ+QLYsg7GBGjtasLnEMjCIrWBgyAZ7058FI9x1SoFEnTCDsCyIhynPILYYSFgbYpUDA5bpQBluXzxpI1yYAbd2sCMYRhwAAHB9ZPztbuMUAAAAAElFTkSuQmCC",
|
||||
spider64 = "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";
|
||||
|
||||
// Favicon
|
||||
document.querySelector("link[rel=icon]").setAttribute("href", "data:image/png;base64," + spider16);
|
||||
|
||||
// Bake button
|
||||
document.querySelector("#bake img").setAttribute("src", "data:image/png;base64," + spider32);
|
||||
|
||||
// About box
|
||||
document.querySelector(".about-img-left").setAttribute("src", "data:image/png;base64," + spider64);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Replaces all instances of the word "cyber" with "spider".
|
||||
* #spiderchef
|
||||
*/
|
||||
SeasonalWaiter.prototype.insertSpiderText = function() {
|
||||
// Title
|
||||
document.title = document.title.replace(/Cyber/g, "Spider");
|
||||
|
||||
// Body
|
||||
SeasonalWaiter.treeWalk(document.body, function(node) {
|
||||
// process only text nodes
|
||||
if (node.nodeType === 3) {
|
||||
node.nodeValue = node.nodeValue.replace(/Cyber/g, "Spider");
|
||||
}
|
||||
}, true);
|
||||
|
||||
// Bake button
|
||||
SeasonalWaiter.treeWalk(document.getElementById("bake-group"), function(node) {
|
||||
// process only text nodes
|
||||
if (node.nodeType === 3) {
|
||||
node.nodeValue = node.nodeValue.replace(/Bake/g, "Spin");
|
||||
}
|
||||
}, true);
|
||||
|
||||
// Recipe title
|
||||
document.querySelector("#recipe .title").innerHTML = "Web";
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Listen for the Konami code sequence of keys. Turn the page upside down if they are all heard in
|
||||
* sequence.
|
||||
@@ -102,53 +45,4 @@ SeasonalWaiter.prototype.konamiCodeListener = function(e) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Walks through the entire DOM starting at the specified element and operates on each node.
|
||||
*
|
||||
* @static
|
||||
* @param {element} parent - The DOM node to start from
|
||||
* @param {Function} fn - The callback function to operate on each node
|
||||
* @param {booleam} allNodes - Whether to operate on every node or not
|
||||
*/
|
||||
SeasonalWaiter.treeWalk = (function() {
|
||||
// Create closure for constants
|
||||
const skipTags = {
|
||||
"SCRIPT": true, "IFRAME": true, "OBJECT": true,
|
||||
"EMBED": true, "STYLE": true, "LINK": true, "META": true
|
||||
};
|
||||
|
||||
return function(parent, fn, allNodes) {
|
||||
let node = parent.firstChild;
|
||||
|
||||
while (node && node !== parent) {
|
||||
if (allNodes || node.nodeType === 1) {
|
||||
if (fn(node) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// If it's an element &&
|
||||
// has children &&
|
||||
// has a tagname && is not in the skipTags list
|
||||
// then, we can enumerate children
|
||||
if (node.nodeType === 1 &&
|
||||
node.firstChild &&
|
||||
!(node.tagName && skipTags[node.tagName])) {
|
||||
node = node.firstChild;
|
||||
} else if (node.nextSibling) {
|
||||
node = node.nextSibling;
|
||||
} else {
|
||||
// No child and no nextsibling
|
||||
// Find parent that has a nextSibling
|
||||
while ((node = node.parentNode) !== parent) {
|
||||
if (node.nextSibling) {
|
||||
node = node.nextSibling;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
export default SeasonalWaiter;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -100,6 +100,7 @@
|
||||
<div id="input" class="split no-select">
|
||||
<div class="title no-select">
|
||||
<label for="input-text">Input</label>
|
||||
<div class="loading-icon" style="display: none"></div>
|
||||
<div class="btn-group io-btn-group">
|
||||
<button type="button" class="btn btn-default btn-sm" id="clr-io"><img aria-hidden="true" src="<%- require('../static/images/recycle-16x16.png') %>" alt="Recycle Icon"/> Clear I/O</button>
|
||||
<button type="button" class="btn btn-default btn-sm" id="reset-layout"><img aria-hidden="true" src="<%- require('../static/images/layout-16x16.png') %>" alt="Grid Icon"/> Reset layout</button>
|
||||
@@ -116,6 +117,7 @@
|
||||
<div id="output" class="split">
|
||||
<div class="title no-select">
|
||||
<label for="output-text">Output</label>
|
||||
<div class="loading-icon" style="display: none"></div>
|
||||
<div class="btn-group io-btn-group">
|
||||
<button type="button" class="btn btn-default btn-sm" id="save-to-file" title="Save to file"><img aria-hidden="true" src="<%- require('../static/images/save_as-16x16.png') %>" alt="Save Icon"/> Save to file</button>
|
||||
<button type="button" class="btn btn-default btn-sm" id="switch" title="Move output to input"><img aria-hidden="true" src="<%- require('../static/images/switch-16x16.png') %>" alt="Switch Icon"/> Move output to input</button>
|
||||
|
||||
@@ -38,17 +38,17 @@ import Chef from "../src/core/Chef.js";
|
||||
TestRegister.prototype.runTests = function() {
|
||||
return Promise.all(
|
||||
this.tests.map(function(test, i) {
|
||||
let chef = new Chef();
|
||||
const chef = new Chef();
|
||||
|
||||
return Promise.resolve(chef.bake(
|
||||
return chef.bake(
|
||||
test.input,
|
||||
test.recipeConfig,
|
||||
{},
|
||||
0,
|
||||
false
|
||||
))
|
||||
)
|
||||
.then(function(result) {
|
||||
let ret = {
|
||||
const ret = {
|
||||
test: test,
|
||||
status: null,
|
||||
output: null,
|
||||
|
||||
+9
-8
@@ -16,13 +16,14 @@ import "./tests/operations/ByteRepr.js";
|
||||
import "./tests/operations/Code.js";
|
||||
import "./tests/operations/Compress.js";
|
||||
import "./tests/operations/FlowControl.js";
|
||||
import "./tests/operations/Image.js";
|
||||
import "./tests/operations/MorseCode.js";
|
||||
import "./tests/operations/StrUtils.js";
|
||||
|
||||
let allTestsPassing = true,
|
||||
testStatusCounts = {
|
||||
total: 0,
|
||||
};
|
||||
let allTestsPassing = true;
|
||||
const testStatusCounts = {
|
||||
total: 0,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@@ -32,7 +33,7 @@ let allTestsPassing = true,
|
||||
* @returns {string}
|
||||
*/
|
||||
function statusToIcon(status) {
|
||||
let icons = {
|
||||
const icons = {
|
||||
erroring: "🔥",
|
||||
failing: "❌",
|
||||
passing: "✔️️",
|
||||
@@ -48,7 +49,7 @@ function statusToIcon(status) {
|
||||
*/
|
||||
function handleTestResult(testResult) {
|
||||
allTestsPassing = allTestsPassing && testResult.status === "passing";
|
||||
let newCount = (testStatusCounts[testResult.status] || 0) + 1;
|
||||
const newCount = (testStatusCounts[testResult.status] || 0) + 1;
|
||||
testStatusCounts[testResult.status] = newCount;
|
||||
testStatusCounts.total += 1;
|
||||
|
||||
@@ -83,8 +84,8 @@ TestRegister.runTests()
|
||||
|
||||
console.log("\n");
|
||||
|
||||
for (let testStatus in testStatusCounts) {
|
||||
let count = testStatusCounts[testStatus];
|
||||
for (const testStatus in testStatusCounts) {
|
||||
const count = testStatusCounts[testStatus];
|
||||
if (count > 0) {
|
||||
console.log(testStatus.toUpperCase(), count);
|
||||
}
|
||||
|
||||
@@ -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: [
|
||||
@@ -141,4 +197,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],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Image operation tests.
|
||||
*
|
||||
* @author tlwr [toby@toby.codes]
|
||||
*
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../TestRegister.js";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Extract EXIF: nothing",
|
||||
input: "",
|
||||
expectedOutput: "Found 0 tags.\n",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Extract EXIF",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Extract EXIF: hello world text (error)",
|
||||
input: "hello world",
|
||||
expectedError: true,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Extract EXIF",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Extract EXIF: meerkat jpeg",
|
||||
input: "ffd8ffe12cd645786966000049492a000800000008000f01020004000000534f4e5910010200060000006e0000001a01050001000000740000001b010500010000007c0000002801030001000000020000003101020011000000840000003201020013000000960000006987040001000000aa000000300200004453432d483546000000010000004600000001000000506963746f6d696f20312e322e33312e3000323031303a30373a30342032333a33313a31330018009a82050001000000d00100009d82050001000000d80100002288030001000000030000002788030001000000c80000000090070004000000303232310390020013000000e00100000490020013000000f401000001920500010000000802000002920500010000001002000004920500010000001802000005920500010000002002000007920300010000000500000008920300010000000a0000000992030001000000100000000a920500010000002802000000a30700010000000300000001a30700010000000100000001a40300010000000000000002a40300010000000100000003a40300010000000100000006a40300010000000000000008a40300010000000000000009a4030001000000000000000aa40300010000000000000000000000010000007d000000250000000a000000323030383a30393a30312031333a32343a343600323030383a30393a30312031333a32343a34360043490d0048e801004b9a390040420f00030000000a0000000300000001000000480000000100000006000301030001000000060000001a010500010000007e0200001b010500010000008602000028010300010000000200000001020400010000008e0200000202040001000000402a00000000000048000000010000004800000001000000ffd8ffee000e41646f626500640000000001ffdb0084000604040405040605050609060506090b080606080b0c0a0a0b0a0a0c100c0c0c0c0c0c100c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c010707070d0c0d18101018140e0e0e14140e0e0e0e14110c0c0c0c0c11110c0c0c0c0c0c110c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0cffc0001108009600c803011100021101031101ffdd00040019ffc401a20000000701010101010000000000000000040503020601000708090a0b0100020203010101010100000000000000010002030405060708090a0b1000020103030204020607030402060273010203110400052112314151061361227181143291a10715b14223c152d1e1331662f0247282f12543345392a2b26373c235442793a3b33617546474c3d2e2082683090a181984944546a4b456d355281af2e3f3c4d4e4f465758595a5b5c5d5e5f566768696a6b6c6d6e6f637475767778797a7b7c7d7e7f738485868788898a8b8c8d8e8f82939495969798999a9b9c9d9e9f92a3a4a5a6a7a8a9aaabacadaeafa110002020102030505040506040803036d0100021103042112314105511361220671819132a1b1f014c1d1e1234215526272f1332434438216925325a263b2c20773d235e2448317549308090a18192636451a2764745537f2a3b3c32829d3e3f38494a4b4c4d4e4f465758595a5b5c5d5e5f5465666768696a6b6c6d6e6f6475767778797a7b7c7d7e7f738485868788898a8b8c8d8e8f839495969798999a9b9c9d9e9f92a3a4a5a6a7a8a9aaabacadaeafaffda000c03010002110311003f00068bb671e5ec1500c895b5400786055e0605b5ea299129b545514c16ab82e2b6b80c516b82e295ca315b5c46050b9062825b0a2b8a82adf55b836ef70b133411b057900a852454571a357d11c62ebaa826f51b50f5af862032979ac5708eb0cad546da094f6ff8adbfe346c953ae8e43827c32feee5f47f47fa2b9e4890ee457c303b0b433dcc209a9c09b506d56d83701b9c3c2bc4be1bf59eb6ccb40ff00ddb1e9c8fec9f66cb211b15fe95c5cf5cf9ff3ff00a51412464b1d3e4f85812f68e7a83dd3018f107439316fe19feb6197fbd745752c442c8ed1988f10c3aa7b1ff23fe23950241a29d2eb4e3f44fe8ff728860797271490ee581d9ab92301541da4e24476fde63284bfb58d11a651d0fef546d556f84ed8217f49ff0035d16a3181c94ed4b346fbd67b71461fefc8bfaad7fe0732f04fbf91630f5c2bf8a1fee3fe3aff00ffd00a99c717af541b9c0abc75c8aaa0029810bc0c8b2545c50bc0df155d4c2ad8a57052b7551df0d2b46788756c095a2fe05069b9c6d0a2faa00d451538f35b0ad6be60d46c64f56df70769216dd1d7f95864a13312d5931c6628a672d85bf9821375e5f6105ea0adce8f25036dd5a13fb43db2e38c4b787fa4718669e2353f547f9fff0014904f1dca318a6a823692361423da9d8e5039b7e58472468f229dfe83b6d4b4d8a6d32491b51863a5cdb486a6523a98cff353f63f97098f1721bffba70f067384f873e5fce48dec4f1e5f687ed53a83e0464625cff1624d214daf097971ad7a65b128914472e2952a30128017dc017f685e3dae6021891d6a3bd7df264dfa87f9ceb357a6b143fad8ff00e23fe250a6637aa250804f1ad24a7ed53c464270120e9b248e4dcfd4b6deec5bd15aaf68db78b467dbfc9caa122369391a3d71c468ef044cc11d0c7b3c32a90ac3a6e3a8396bbac9a686685c7aa4a92b251892ae84c335363edfc460eb4f2e4984bb8fd2ff00ffd10a8338e2f5eaa06f8155141c0aa82806e70522da7b98106ec2be18045287975544601456b92114134abfa45a80f1d8f438d23896bdf4bd875c8af129b4b7527d9db088a0cd62add13bb6f87851c4b9ada50b5663538084095a7b169364b60f73688ba85b955fac06052e2dde9bfd93bc7fe5633c46b8a2767132659835f4cbf87f9934b24b6f4c7ab17ef21e95fda53e0c3f8fd9c8c4b660d40c9b7d331fc2a6e4b0fe188e6e5014a96beac12a4d1318e543c91d0d0823b82325747662636c95751d2bcc016df592b65aa00041aa28a249e0b3affc6f97090c9cfd32fe77f39c4962962de1ea8ff33fe252dbed3f54d16f84572a632778e4527830ecc8c3299c4c4d14ce10d4436e63fd8bae1c5f3996e1fd3bb3ff001f4a29ccf8c8077ff2ff00e0b2b323767fd37fc53ac198c7d19072ff004d14bae24961611dec7e993f62e00fddb7d236fbb2e05be3aa303eaf543f9fff0014a77514b1d07246e42a287b1c85efbb990d5e326add6cd2dbc8b3aa6e36643d194f5072709f0972270138d21af81b2bb5beb6158243fbc4f0af63f3c998806ba179ccf78f271ffa78fe3f9ebef204a0bb87e2b79802c3b0aff03954a025b1e6babd38006487d13ff62820cf6a76abdab1fb3dc1febff12caa3220d16ad26b27825b7d3fcd53d4214172b223036f7c940dd848b4ebe06bc72d98dafb975e2329f1c7e9c9eaff0039ffd2297d4d15cLine truncated
|
||||
expectedOutput: [
|
||||
"Found 28 tags.",
|
||||
"",
|
||||
"Make: SONY",
|
||||
"Model: DSC-H5",
|
||||
"XResolution: 72",
|
||||
"YResolution: 72",
|
||||
"ResolutionUnit: 2",
|
||||
"Software: Pictomio 1.2.31.0",
|
||||
"ModifyDate: 2010:07:04 23:31:13",
|
||||
"ExposureTime: 0.008",
|
||||
"FNumber: 3.7",
|
||||
"ExposureProgram: 3",
|
||||
"ISO: 200",
|
||||
"DateTimeOriginal: 1220275486",
|
||||
"CreateDate: 1220275486",
|
||||
"ShutterSpeedValue: 6.965784",
|
||||
"ApertureValue: 3.775051",
|
||||
"ExposureCompensation: 0.3",
|
||||
"MaxApertureValue: 3",
|
||||
"MeteringMode: 5",
|
||||
"LightSource: 10",
|
||||
"Flash: 16",
|
||||
"FocalLength: 72",
|
||||
"CustomRendered: 0",
|
||||
"ExposureMode: 1",
|
||||
"WhiteBalance: 1",
|
||||
"SceneCaptureType: 0",
|
||||
"Contrast: 0",
|
||||
"Saturation: 0",
|
||||
"Sharpness: 0",
|
||||
].join("\n"),
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Hex",
|
||||
args: ["None"]
|
||||
},
|
||||
{
|
||||
op: "Extract EXIF",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Extract EXIF: avatar jpeg",
|
||||
input: "ffd8ffdb008400080606070605080707070909080a0c140d0c0b0b0c1912130f141d1a1f1e1d1a1c1c20242e2720222c231c1c2837292c30313434341f27393d38323c2e333432010909090c0b0c180d0d1832211c213232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232ffc00011080058005803012200021101031101ffc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00f39d2237f3223f398e3765047cb8c95ee0f1d6b73c66037852c2786479049772073d7e652ca09ef9da1067bedef597e15b382ffcd6b89248e08d1a57208c95040c73c67381fad6f6a176ff00f08d491adbdb5bc12cd3b24580f85405b71cf392463230319ae09de334cf16ab71aa9f6397b2bb824d34690d641e6ba789d67de4ec55dc08db8e739ffc77a77ad6f14c7e4e8f3a12c156ed157e5c06468f7af18e0ae48fc4d72d2cc069ab2c4e1255941054004647b723fce2afbea4b35bc534c91ce63456944a5fe7cf1cfcdea2b4941b69f6379d36e4a48c270c08638e4e79c1a78cb49c138dfcae00ae96d239b5dba8ec2db4eb37915c6c2a879382704e7a7afa558d6f4fb7d1960b696dedbce89dbed3322ee5dc73b541ce7181d7dcfb55f3f4ea5fb7d795ad4e6e20de747f7940751b94e0823dfd7393f8557946f676ddb882492ddfd4d74497da4b3208e21bd54b380a4ab10383c9fef13dbd2b2afedada1b62f1caccce0328ddca8c904118f507d288b77d8709b6f556330f2d96ce3a015bda8d9a4766080b1b9c30dc39202038f6ebfad610daad8dd93d2ba0d5ee20480c250b398c6370230c55727f43c7b83454bdd58755be68a464a320310192cbcb7bf3c7d2ad79c3fe790ffbec5500c1ee03e554f650381cf4ab7e637aaffdf34e511ca26ef871e38f4c2b72ec90dcc7246c40e87820f1fed0157ee58c1e1f8e1701fcfb7bb73701b2782c17278e4e31f8d67787525bab3b6b5891371903296c839049233d8607a7f2a97c617d6443450cafbc41e4f979cedc480e4e3f88e1f3e84d6325cd3b1c938f355e5f3ff339b1103a0ceed8c89230b93cf20f6ff3d2b4edec74a78205b9bc811c4232584ae33d7b0c743eb546403fe11e90855199a2048ebf75bfcfe745a45e749045bbefe130173d87f3ade5b5cea95da7adb5fd0f45d3751d3f4ad16eff00b25650af84fb43a005c842c4a8c7ca33dbdfd79a5b1d60ae9aaef70ce59fe645c640c77eb8f5ed54b50b29345f0d258c36acf700abf9dbb2a176307e339c92c3f2ac981e5b8d26178623b3cdd8caa48f9b1d40fcff004af1e718d6bcaf7573c274a15ef34ee9b23d62cac6fa39aeac24115c451ab02bf2994606e040ee3afe159fae5d68973046d60eff00687b687cc528c01942fef0e48ee467d39ae834ad2ede7d3cdc4c678eecec4822519121dca1b771c0c67b8fc78ae28c6ff69103e4183cc420f452303f9d77e167bc2f7b1e9611ef0bdec5265c3b1381c9ad2d5a4f308da98071839cf1b4553995448e064904d68eaa85d62906d0b21200ce4f0060febfa57549ea8ed93f7a2674118f35493d4f738ef5a1e5c7ea2a8daa05ba0092464638f715b388ff00baff0097ff005aa66f514e5a973c2d7690de69ee085114d997cb3c84dc324e781c1c0fcbbd67f8b12d12ee21668c525432f98c319dc49c019ec49ebcd47a6ca122910a9632945dc075f99491fa51ae4cb75696770a71b435bb26dc7cc9b4023eaa53f106a52b4ccd46d56e432b29f0dedd8438953e6f6c356cf83a289b534bc755cc4b84c91f78a63fa1ac09a50da394c6312263fef8357bc3b7820bd87cc70a98663ce390b91fd696222dd29242c44252a334ba9deea69fdadaed858c7ff1eed3624562402157241f6e2ae8f045b2ea4f7104b2a83f398a33f2ff00fab9e94786d22bbf10ea17330c24312c28ad93b778dcdd47a103f3adbbb92ce0b416af6eec8b242d12a9daa02306c75f415f3739384a34a32b7f57ff0023e5a729539468c25cbdfe7aff0091c668ccb040efd337b2246a4fdd507a7f21f8572dacc022d55e751b7ed11173df2df2e7fc7f1aded5266b5d64c31102d64b93329c1ea71b81ff003deb06eaf61943b48c15a3b726204fdf62ca08c7d39fcabd3c2425ed1545d51ebe0e12f69ed57da4624aaf966e0f3cfeb52dd2954419184381f2f3d05432c81d9f039dd525e3670dcfcc7238ed815eb3bdd1ec6ba1108df7a32fca4fcd9f6ab3be7ff9eaf4e28c90db3908721bd39edf851bff00d95ffbed6a1b6c4e4c8d09f29b24001b8c724d47733a3e9f1c25b748269246c0e06768fcfe53fa540642c140e0f5a85f00c9c9232467f1ab51d4d1435279189d373dbcdc0ffbe6b5bc3da59d46e115c95b645dccfbb1db381ee6b24465f4c8c2ae59a7238ea785c0fd6bd074cb78b4ed36ce348b6489bdda403efb6d1819faf4ed58e2eafb38596ece6c6d6f654da8eecdfb4bcd1747b4bab97b88fcc91cccf1abee21b0011d4ff7455af0f78874bf107da229191368f944bfc5e983eb9ae07c4897579abb2b4437f92dd0e78cb1e4f6ea063f0ad34b77b6d2e196097c989275dcc0fcb8cf07df91d3fc6bc6960a125cf277933c196029b5ed24ef264fabe971b6a0f34d231637281610782bb179cfae4f35c36b9118278d55711053b4eece7393f875aeb2d01934fb58be4dd34b82fdf3c6093ebcd50f12e9d35a68c84aab2994124804ed2a0a9cf6aedc34dc26a127e477e12aba7523093f238f0c431ee2ac5c1dc839c76e9ec2aae3939e2accdf740230700f1f415eabdd1ed496a89d9898d12304b1183c74e9f9537ecf71fdc6ff00bee9f02949c646723af07d2aef3ea3fef81fe358b958c5cb976326509bd4c671c66a13cc9202d9249008e879a9a28fcc915482d8c74f4f4Line truncated
|
||||
expectedOutput: "Found 0 tags.\n",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "From Hex",
|
||||
args: ["None"]
|
||||
},
|
||||
{
|
||||
op: "Extract EXIF",
|
||||
args: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
Reference in new issue
Block a user