mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-22 15:56:16 -04:00
Merge remote-tracking branch 'upstream/master' into ssh-host-key
Bring up to date with master
This commit is contained in:
commit
1cdcaebb4d
540 changed files with 30965 additions and 5829 deletions
|
@ -4,9 +4,10 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Dish from "./Dish";
|
||||
import Recipe from "./Recipe";
|
||||
import Dish from "./Dish.mjs";
|
||||
import Recipe from "./Recipe.mjs";
|
||||
import log from "loglevel";
|
||||
import { isWorkerEnvironment } from "./Utils.mjs";
|
||||
|
||||
/**
|
||||
* The main controller for CyberChef.
|
||||
|
@ -28,8 +29,6 @@ class Chef {
|
|||
* @param {Object[]} recipeConfig - The recipe configuration object
|
||||
* @param {Object} options - The options object storing various user choices
|
||||
* @param {boolean} options.attempHighlight - Whether or not to attempt highlighting
|
||||
* @param {number} progress - The position in the recipe to start from
|
||||
* @param {number} [step] - Whether to only execute one operation in the recipe
|
||||
*
|
||||
* @returns {Object} response
|
||||
* @returns {string} response.result - The output of the recipe
|
||||
|
@ -38,46 +37,20 @@ class Chef {
|
|||
* @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)
|
||||
*/
|
||||
async bake(input, recipeConfig, options, progress, step) {
|
||||
async bake(input, recipeConfig, options) {
|
||||
log.debug("Chef baking");
|
||||
const startTime = new Date().getTime(),
|
||||
recipe = new Recipe(recipeConfig),
|
||||
containsFc = recipe.containsFlowControl(),
|
||||
notUTF8 = options && options.hasOwnProperty("treatAsUtf8") && !options.treatAsUtf8;
|
||||
let error = false;
|
||||
|
||||
if (containsFc && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
|
||||
|
||||
// Clean up progress
|
||||
if (progress >= recipeConfig.length) {
|
||||
notUTF8 = options && "treatAsUtf8" in options && !options.treatAsUtf8;
|
||||
let error = false,
|
||||
progress = 0;
|
||||
}
|
||||
|
||||
if (step) {
|
||||
// Unset breakpoint on this step
|
||||
recipe.setBreakpoint(progress, false);
|
||||
// Set breakpoint on next step
|
||||
recipe.setBreakpoint(progress + 1, true);
|
||||
}
|
||||
if (containsFc && isWorkerEnvironment()) self.setOption("attemptHighlight", false);
|
||||
|
||||
// If the previously run operation presented a different value to its
|
||||
// normal output, we need to recalculate it.
|
||||
if (recipe.lastOpPresented(progress)) {
|
||||
progress = 0;
|
||||
}
|
||||
|
||||
// If stepping with flow control, we have to start from the beginning
|
||||
// but still want to skip all previous breakpoints
|
||||
if (progress > 0 && containsFc) {
|
||||
recipe.removeBreaksUpTo(progress);
|
||||
progress = 0;
|
||||
}
|
||||
|
||||
// If starting from scratch, load data
|
||||
if (progress === 0) {
|
||||
const type = input instanceof ArrayBuffer ? Dish.ARRAY_BUFFER : Dish.STRING;
|
||||
this.dish.set(input, type);
|
||||
}
|
||||
// Load data
|
||||
const type = input instanceof ArrayBuffer ? Dish.ARRAY_BUFFER : Dish.STRING;
|
||||
this.dish.set(input, type);
|
||||
|
||||
try {
|
||||
progress = await recipe.execute(this.dish, progress);
|
||||
|
@ -196,6 +169,18 @@ class Chef {
|
|||
return await newDish.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the title of a dish and returns it
|
||||
*
|
||||
* @param {Dish} dish
|
||||
* @param {number} [maxLength=100]
|
||||
* @returns {string}
|
||||
*/
|
||||
async getDishTitle(dish, maxLength=100) {
|
||||
const newDish = new Dish(dish);
|
||||
return await newDish.getTitle(maxLength);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Chef;
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Chef from "./Chef";
|
||||
import Chef from "./Chef.mjs";
|
||||
import OperationConfig from "./config/OperationConfig.json";
|
||||
import OpModules from "./config/modules/OpModules";
|
||||
import OpModules from "./config/modules/OpModules.mjs";
|
||||
|
||||
// Add ">" to the start of all log messages in the Chef Worker
|
||||
import loglevelMessagePrefix from "loglevel-message-prefix";
|
||||
|
@ -25,6 +25,8 @@ self.chef = new Chef();
|
|||
|
||||
self.OpModules = OpModules;
|
||||
self.OperationConfig = OperationConfig;
|
||||
self.inputNum = -1;
|
||||
|
||||
|
||||
// Tell the app that the worker has loaded and is ready to operate
|
||||
self.postMessage({
|
||||
|
@ -35,6 +37,9 @@ self.postMessage({
|
|||
/**
|
||||
* Respond to message from parent thread.
|
||||
*
|
||||
* inputNum is optional and only used for baking multiple inputs.
|
||||
* Defaults to -1 when one isn't sent with the bake message.
|
||||
*
|
||||
* Messages should have the following format:
|
||||
* {
|
||||
* action: "bake" | "silentBake",
|
||||
|
@ -43,8 +48,9 @@ self.postMessage({
|
|||
* recipeConfig: {[Object]},
|
||||
* options: {Object},
|
||||
* progress: {number},
|
||||
* step: {boolean}
|
||||
* } | undefined
|
||||
* step: {boolean},
|
||||
* [inputNum=-1]: {number}
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
self.addEventListener("message", function(e) {
|
||||
|
@ -62,6 +68,9 @@ self.addEventListener("message", function(e) {
|
|||
case "getDishAs":
|
||||
getDishAs(r.data);
|
||||
break;
|
||||
case "getDishTitle":
|
||||
getDishTitle(r.data);
|
||||
break;
|
||||
case "docURL":
|
||||
// Used to set the URL of the current document so that scripts can be
|
||||
// imported into an inline worker.
|
||||
|
@ -91,30 +100,35 @@ self.addEventListener("message", function(e) {
|
|||
async function bake(data) {
|
||||
// Ensure the relevant modules are loaded
|
||||
self.loadRequiredModules(data.recipeConfig);
|
||||
|
||||
try {
|
||||
self.inputNum = (data.inputNum !== undefined) ? data.inputNum : -1;
|
||||
const response = await self.chef.bake(
|
||||
data.input, // The user's input
|
||||
data.recipeConfig, // The configuration of the recipe
|
||||
data.options, // Options set by the user
|
||||
data.progress, // The current position in the recipe
|
||||
data.step // Whether or not to take one step or execute the whole recipe
|
||||
data.options // Options set by the user
|
||||
);
|
||||
|
||||
const transferable = (data.input instanceof ArrayBuffer) ? [data.input] : undefined;
|
||||
self.postMessage({
|
||||
action: "bakeComplete",
|
||||
data: Object.assign(response, {
|
||||
id: data.id
|
||||
id: data.id,
|
||||
inputNum: data.inputNum,
|
||||
bakeId: data.bakeId
|
||||
})
|
||||
});
|
||||
}, transferable);
|
||||
|
||||
} catch (err) {
|
||||
self.postMessage({
|
||||
action: "bakeError",
|
||||
data: Object.assign(err, {
|
||||
id: data.id
|
||||
})
|
||||
data: {
|
||||
error: err.message || err,
|
||||
id: data.id,
|
||||
inputNum: data.inputNum
|
||||
}
|
||||
});
|
||||
}
|
||||
self.inputNum = -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -136,13 +150,33 @@ function silentBake(data) {
|
|||
*/
|
||||
async function getDishAs(data) {
|
||||
const value = await self.chef.getDishAs(data.dish, data.type);
|
||||
|
||||
const transferable = (data.type === "ArrayBuffer") ? [value] : undefined;
|
||||
self.postMessage({
|
||||
action: "dishReturned",
|
||||
data: {
|
||||
value: value,
|
||||
id: data.id
|
||||
}
|
||||
}, transferable);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the dish title
|
||||
*
|
||||
* @param {object} data
|
||||
* @param {Dish} data.dish
|
||||
* @param {number} data.maxLength
|
||||
* @param {number} data.id
|
||||
*/
|
||||
async function getDishTitle(data) {
|
||||
const title = await self.chef.getDishTitle(data.dish, data.maxLength);
|
||||
self.postMessage({
|
||||
action: "dishReturned",
|
||||
data: {
|
||||
value: title,
|
||||
id: data.id
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -175,7 +209,7 @@ self.loadRequiredModules = function(recipeConfig) {
|
|||
recipeConfig.forEach(op => {
|
||||
const module = self.OperationConfig[op.op].module;
|
||||
|
||||
if (!OpModules.hasOwnProperty(module)) {
|
||||
if (!(module in OpModules)) {
|
||||
log.info(`Loading ${module} module`);
|
||||
self.sendStatusMessage(`Loading ${module} module`);
|
||||
self.importScripts(`${self.docURL}/modules/${module}.js`);
|
||||
|
@ -193,7 +227,28 @@ self.loadRequiredModules = function(recipeConfig) {
|
|||
self.sendStatusMessage = function(msg) {
|
||||
self.postMessage({
|
||||
action: "statusMessage",
|
||||
data: msg
|
||||
data: {
|
||||
message: msg,
|
||||
inputNum: self.inputNum
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Send progress update to the app.
|
||||
*
|
||||
* @param {number} progress
|
||||
* @param {number} total
|
||||
*/
|
||||
self.sendProgressMessage = function(progress, total) {
|
||||
self.postMessage({
|
||||
action: "progressMessage",
|
||||
data: {
|
||||
progress: progress,
|
||||
total: total,
|
||||
inputNum: self.inputNum
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -5,11 +5,22 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Utils from "./Utils";
|
||||
import DishError from "./errors/DishError";
|
||||
import Utils, { isNodeEnvironment } from "./Utils.mjs";
|
||||
import DishError from "./errors/DishError.mjs";
|
||||
import BigNumber from "bignumber.js";
|
||||
import { detectFileType } from "./lib/FileType.mjs";
|
||||
import log from "loglevel";
|
||||
|
||||
import DishByteArray from "./dishTypes/DishByteArray.mjs";
|
||||
import DishBigNumber from "./dishTypes/DishBigNumber.mjs";
|
||||
import DishFile from "./dishTypes/DishFile.mjs";
|
||||
import DishHTML from "./dishTypes/DishHTML.mjs";
|
||||
import DishJSON from "./dishTypes/DishJSON.mjs";
|
||||
import DishListFile from "./dishTypes/DishListFile.mjs";
|
||||
import DishNumber from "./dishTypes/DishNumber.mjs";
|
||||
import DishString from "./dishTypes/DishString.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* The data being operated on by each operation.
|
||||
*/
|
||||
|
@ -18,16 +29,27 @@ class Dish {
|
|||
/**
|
||||
* Dish constructor
|
||||
*
|
||||
* @param {Dish} [dish=null] - A dish to clone
|
||||
* @param {Dish || *} [dishOrInput=null] - A dish to clone OR an object
|
||||
* literal to make into a dish
|
||||
* @param {Enum} [type=null] (optional) - A type to accompany object
|
||||
* literal input
|
||||
*/
|
||||
constructor(dish=null) {
|
||||
constructor(dishOrInput=null, type = null) {
|
||||
this.value = new ArrayBuffer(0);
|
||||
this.type = Dish.ARRAY_BUFFER;
|
||||
|
||||
if (dish &&
|
||||
dish.hasOwnProperty("value") &&
|
||||
dish.hasOwnProperty("type")) {
|
||||
this.set(dish.value, dish.type);
|
||||
// Case: dishOrInput is dish object
|
||||
if (dishOrInput &&
|
||||
Object.prototype.hasOwnProperty.call(dishOrInput, "value") &&
|
||||
Object.prototype.hasOwnProperty.call(dishOrInput, "type")) {
|
||||
this.set(dishOrInput.value, dishOrInput.type);
|
||||
// input and type defined separately
|
||||
} else if (dishOrInput && type !== null) {
|
||||
this.set(dishOrInput, type);
|
||||
// No type declared, so infer it.
|
||||
} else if (dishOrInput) {
|
||||
const inferredType = Dish.typeEnum(dishOrInput.constructor.name);
|
||||
this.set(dishOrInput, inferredType);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,6 +78,7 @@ class Dish {
|
|||
case "big number":
|
||||
return Dish.BIG_NUMBER;
|
||||
case "json":
|
||||
case "object": // object constructor name. To allow JSON input in node.
|
||||
return Dish.JSON;
|
||||
case "file":
|
||||
return Dish.FILE;
|
||||
|
@ -99,6 +122,43 @@ class Dish {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value of the data in the type format specified.
|
||||
*
|
||||
* If running in a browser, get is asynchronous.
|
||||
*
|
||||
* @param {number} type - The data type of value, see Dish enums.
|
||||
* @param {boolean} [notUTF8=false] - Do not treat strings as UTF8.
|
||||
* @returns {* | Promise} - (Broswer) A promise | (Node) value of dish in given type
|
||||
*/
|
||||
get(type, notUTF8=false) {
|
||||
if (typeof type === "string") {
|
||||
type = Dish.typeEnum(type);
|
||||
}
|
||||
|
||||
if (this.type !== type) {
|
||||
|
||||
// Node environment => _translate is sync
|
||||
if (isNodeEnvironment()) {
|
||||
this._translate(type, notUTF8);
|
||||
return this.value;
|
||||
|
||||
// Browser environment => _translate is async
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._translate(type, notUTF8)
|
||||
.then(() => {
|
||||
resolve(this.value);
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this.value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the data value and type and then validates them.
|
||||
*
|
||||
|
@ -122,116 +182,81 @@ class Dish {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value of the data in the type format specified.
|
||||
* Returns the Dish as the given type, without mutating the original dish.
|
||||
*
|
||||
* If running in a browser, get is asynchronous.
|
||||
*
|
||||
* @Node
|
||||
*
|
||||
* @param {number} type - The data type of value, see Dish enums.
|
||||
* @param {boolean} [notUTF8=false] - Do not treat strings as UTF8.
|
||||
* @returns {*} - The value of the output data.
|
||||
* @returns {Dish | Promise} - (Broswer) A promise | (Node) value of dish in given type
|
||||
*/
|
||||
async get(type, notUTF8=false) {
|
||||
if (typeof type === "string") {
|
||||
type = Dish.typeEnum(type);
|
||||
}
|
||||
if (this.type !== type) {
|
||||
await this._translate(type, notUTF8);
|
||||
}
|
||||
return this.value;
|
||||
presentAs(type, notUTF8=false) {
|
||||
const clone = this.clone();
|
||||
return clone.get(type, notUTF8);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Translates the data to the given type format.
|
||||
*
|
||||
* @param {number} toType - The data type of value, see Dish enums.
|
||||
* @param {boolean} [notUTF8=false] - Do not treat strings as UTF8.
|
||||
* Detects the MIME type of the current dish
|
||||
* @returns {string}
|
||||
*/
|
||||
async _translate(toType, notUTF8=false) {
|
||||
log.debug(`Translating Dish from ${Dish.enumLookup(this.type)} to ${Dish.enumLookup(toType)}`);
|
||||
detectDishType() {
|
||||
const data = new Uint8Array(this.value.slice(0, 2048)),
|
||||
types = detectFileType(data);
|
||||
|
||||
// Convert data to intermediate ArrayBuffer type
|
||||
try {
|
||||
switch (this.type) {
|
||||
case Dish.STRING:
|
||||
this.value = this.value ? Utils.strToArrayBuffer(this.value) : new ArrayBuffer;
|
||||
break;
|
||||
case Dish.NUMBER:
|
||||
this.value = typeof this.value === "number" ? Utils.strToArrayBuffer(this.value.toString()) : new ArrayBuffer;
|
||||
break;
|
||||
case Dish.HTML:
|
||||
this.value = this.value ? Utils.strToArrayBuffer(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : new ArrayBuffer;
|
||||
break;
|
||||
case Dish.BYTE_ARRAY:
|
||||
this.value = new Uint8Array(this.value).buffer;
|
||||
break;
|
||||
case Dish.BIG_NUMBER:
|
||||
this.value = BigNumber.isBigNumber(this.value) ? Utils.strToArrayBuffer(this.value.toFixed()) : new ArrayBuffer;
|
||||
break;
|
||||
case Dish.JSON:
|
||||
this.value = this.value ? Utils.strToArrayBuffer(JSON.stringify(this.value, null, 4)) : new ArrayBuffer;
|
||||
break;
|
||||
case Dish.FILE:
|
||||
this.value = (await Utils.readFile(this.value)).buffer;
|
||||
break;
|
||||
case Dish.LIST_FILE:
|
||||
this.value = await Promise.all(this.value.map(async f => Utils.readFile(f)));
|
||||
this.value = concatenateTypedArrays(...this.value).buffer;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
throw new DishError(`Error translating from ${Dish.enumLookup(this.type)} to ArrayBuffer: ${err}`);
|
||||
}
|
||||
|
||||
this.type = Dish.ARRAY_BUFFER;
|
||||
|
||||
// Convert from ArrayBuffer to toType
|
||||
try {
|
||||
switch (toType) {
|
||||
case Dish.STRING:
|
||||
case Dish.HTML:
|
||||
this.value = this.value ? Utils.arrayBufferToStr(this.value, !notUTF8) : "";
|
||||
this.type = Dish.STRING;
|
||||
break;
|
||||
case Dish.NUMBER:
|
||||
this.value = this.value ? parseFloat(Utils.arrayBufferToStr(this.value, !notUTF8)) : 0;
|
||||
this.type = Dish.NUMBER;
|
||||
break;
|
||||
case Dish.BYTE_ARRAY:
|
||||
this.value = Array.prototype.slice.call(new Uint8Array(this.value));
|
||||
this.type = Dish.ARRAY_BUFFER;
|
||||
break;
|
||||
case Dish.BIG_NUMBER:
|
||||
try {
|
||||
this.value = new BigNumber(Utils.arrayBufferToStr(this.value, !notUTF8));
|
||||
} catch (err) {
|
||||
this.value = new BigNumber(NaN);
|
||||
}
|
||||
this.type = Dish.BIG_NUMBER;
|
||||
break;
|
||||
case Dish.JSON:
|
||||
this.value = JSON.parse(Utils.arrayBufferToStr(this.value, !notUTF8));
|
||||
this.type = Dish.JSON;
|
||||
break;
|
||||
case Dish.FILE:
|
||||
this.value = new File(this.value, "unknown");
|
||||
this.type = Dish.FILE;
|
||||
break;
|
||||
case Dish.LIST_FILE:
|
||||
this.value = [new File(this.value, "unknown")];
|
||||
this.type = Dish.LIST_FILE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
throw new DishError(`Error translating from ArrayBuffer to ${Dish.enumLookup(toType)}: ${err}`);
|
||||
if (!types.length || !types[0].mime || !types[0].mime === "text/plain") {
|
||||
return null;
|
||||
} else {
|
||||
return types[0].mime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the title of the data up to the specified length
|
||||
*
|
||||
* @param {number} maxLength - The maximum title length
|
||||
* @returns {string}
|
||||
*/
|
||||
async getTitle(maxLength) {
|
||||
let title = "";
|
||||
let cloned;
|
||||
|
||||
switch (this.type) {
|
||||
case Dish.FILE:
|
||||
title = this.value.name;
|
||||
break;
|
||||
case Dish.LIST_FILE:
|
||||
title = `${this.value.length} file(s)`;
|
||||
break;
|
||||
case Dish.JSON:
|
||||
title = "application/json";
|
||||
break;
|
||||
case Dish.NUMBER:
|
||||
case Dish.BIG_NUMBER:
|
||||
title = this.value.toString();
|
||||
break;
|
||||
case Dish.ARRAY_BUFFER:
|
||||
case Dish.BYTE_ARRAY:
|
||||
title = this.detectDishType();
|
||||
if (title !== null) break;
|
||||
// fall through if no mime type was detected
|
||||
default:
|
||||
try {
|
||||
cloned = this.clone();
|
||||
cloned.value = cloned.value.slice(0, 256);
|
||||
title = await cloned.get(Dish.STRING);
|
||||
} catch (err) {
|
||||
log.error(`${Dish.enumLookup(this.type)} cannot be sliced. ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
return title.slice(0, maxLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the value is the type that has been specified.
|
||||
* May have to disable parts of BYTE_ARRAY validation if it effects performance.
|
||||
|
@ -241,7 +266,7 @@ class Dish {
|
|||
valid() {
|
||||
switch (this.type) {
|
||||
case Dish.BYTE_ARRAY:
|
||||
if (!(this.value instanceof Array)) {
|
||||
if (!(this.value instanceof Uint8Array) && !(this.value instanceof Array)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -262,7 +287,21 @@ class Dish {
|
|||
case Dish.ARRAY_BUFFER:
|
||||
return this.value instanceof ArrayBuffer;
|
||||
case Dish.BIG_NUMBER:
|
||||
return BigNumber.isBigNumber(this.value);
|
||||
if (BigNumber.isBigNumber(this.value)) return true;
|
||||
/*
|
||||
If a BigNumber is passed between WebWorkers it is serialised as a JSON
|
||||
object with a coefficient (c), exponent (e) and sign (s). We detect this
|
||||
and reinitialise it as a BigNumber object.
|
||||
*/
|
||||
if (Object.keys(this.value).sort().equals(["c", "e", "s"])) {
|
||||
const temp = new BigNumber();
|
||||
temp.c = this.value.c;
|
||||
temp.e = this.value.e;
|
||||
temp.s = this.value.s;
|
||||
this.value = temp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case Dish.JSON:
|
||||
// All values can be serialised in some manner, so we return true in all cases
|
||||
return true;
|
||||
|
@ -369,26 +408,109 @@ class Dish {
|
|||
return newDish;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Translates the data to the given type format.
|
||||
*
|
||||
* If running in the browser, _translate is asynchronous.
|
||||
*
|
||||
* @param {number} toType - The data type of value, see Dish enums.
|
||||
* @param {boolean} [notUTF8=false] - Do not treat strings as UTF8.
|
||||
* @returns {Promise || undefined}
|
||||
*/
|
||||
_translate(toType, notUTF8=false) {
|
||||
log.debug(`Translating Dish from ${Dish.enumLookup(this.type)} to ${Dish.enumLookup(toType)}`);
|
||||
|
||||
// Node environment => translate is sync
|
||||
if (isNodeEnvironment()) {
|
||||
this._toArrayBuffer();
|
||||
this.type = Dish.ARRAY_BUFFER;
|
||||
this._fromArrayBuffer(toType, notUTF8);
|
||||
|
||||
// Browser environment => translate is async
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._toArrayBuffer()
|
||||
.then(() => this.type = Dish.ARRAY_BUFFER)
|
||||
.then(() => {
|
||||
this._fromArrayBuffer(toType);
|
||||
resolve();
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates a list of Uint8Arrays together
|
||||
*
|
||||
* @param {Uint8Array[]} arrays
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
function concatenateTypedArrays(...arrays) {
|
||||
let totalLength = 0;
|
||||
for (const arr of arrays) {
|
||||
totalLength += arr.length;
|
||||
}
|
||||
const result = new Uint8Array(totalLength);
|
||||
let offset = 0;
|
||||
for (const arr of arrays) {
|
||||
result.set(arr, offset);
|
||||
offset += arr.length;
|
||||
|
||||
/**
|
||||
* Convert this.value to an ArrayBuffer
|
||||
*
|
||||
* If running in a browser, _toByteArray is asynchronous.
|
||||
*
|
||||
* @returns {Promise || undefined}
|
||||
*/
|
||||
_toArrayBuffer() {
|
||||
// Using 'bind' here to allow this.value to be mutated within translation functions
|
||||
const toByteArrayFuncs = {
|
||||
browser: {
|
||||
[Dish.STRING]: () => Promise.resolve(DishString.toArrayBuffer.bind(this)()),
|
||||
[Dish.NUMBER]: () => Promise.resolve(DishNumber.toArrayBuffer.bind(this)()),
|
||||
[Dish.HTML]: () => Promise.resolve(DishHTML.toArrayBuffer.bind(this)()),
|
||||
[Dish.ARRAY_BUFFER]: () => Promise.resolve(),
|
||||
[Dish.BIG_NUMBER]: () => Promise.resolve(DishBigNumber.toArrayBuffer.bind(this)()),
|
||||
[Dish.JSON]: () => Promise.resolve(DishJSON.toArrayBuffer.bind(this)()),
|
||||
[Dish.FILE]: () => DishFile.toArrayBuffer.bind(this)(),
|
||||
[Dish.LIST_FILE]: () => Promise.resolve(DishListFile.toArrayBuffer.bind(this)()),
|
||||
[Dish.BYTE_ARRAY]: () => Promise.resolve(DishByteArray.toArrayBuffer.bind(this)()),
|
||||
},
|
||||
node: {
|
||||
[Dish.STRING]: () => DishString.toArrayBuffer.bind(this)(),
|
||||
[Dish.NUMBER]: () => DishNumber.toArrayBuffer.bind(this)(),
|
||||
[Dish.HTML]: () => DishHTML.toArrayBuffer.bind(this)(),
|
||||
[Dish.ARRAY_BUFFER]: () => {},
|
||||
[Dish.BIG_NUMBER]: () => DishBigNumber.toArrayBuffer.bind(this)(),
|
||||
[Dish.JSON]: () => DishJSON.toArrayBuffer.bind(this)(),
|
||||
[Dish.FILE]: () => DishFile.toArrayBuffer.bind(this)(),
|
||||
[Dish.LIST_FILE]: () => DishListFile.toArrayBuffer.bind(this)(),
|
||||
[Dish.BYTE_ARRAY]: () => DishByteArray.toArrayBuffer.bind(this)(),
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
return toByteArrayFuncs[isNodeEnvironment() && "node" || "browser"][this.type]();
|
||||
} catch (err) {
|
||||
throw new DishError(`Error translating from ${Dish.enumLookup(this.type)} to ArrayBuffer: ${err}`);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
/**
|
||||
* Convert this.value to the given type from ArrayBuffer
|
||||
*
|
||||
* @param {number} toType - the Dish enum to convert to
|
||||
* @param {boolean} [notUTF8=false] - Do not treat strings as UTF8.
|
||||
*/
|
||||
_fromArrayBuffer(toType, notUTF8) {
|
||||
|
||||
// Using 'bind' here to allow this.value to be mutated within translation functions
|
||||
const toTypeFunctions = {
|
||||
[Dish.STRING]: () => DishString.fromArrayBuffer.bind(this)(notUTF8),
|
||||
[Dish.NUMBER]: () => DishNumber.fromArrayBuffer.bind(this)(notUTF8),
|
||||
[Dish.HTML]: () => DishHTML.fromArrayBuffer.bind(this)(notUTF8),
|
||||
[Dish.ARRAY_BUFFER]: () => {},
|
||||
[Dish.BIG_NUMBER]: () => DishBigNumber.fromArrayBuffer.bind(this)(notUTF8),
|
||||
[Dish.JSON]: () => DishJSON.fromArrayBuffer.bind(this)(notUTF8),
|
||||
[Dish.FILE]: () => DishFile.fromArrayBuffer.bind(this)(),
|
||||
[Dish.LIST_FILE]: () => DishListFile.fromArrayBuffer.bind(this)(),
|
||||
[Dish.BYTE_ARRAY]: () => DishByteArray.fromArrayBuffer.bind(this)(),
|
||||
};
|
||||
|
||||
try {
|
||||
toTypeFunctions[toType]();
|
||||
this.type = toType;
|
||||
} catch (err) {
|
||||
throw new DishError(`Error translating from ArrayBuffer to ${Dish.enumLookup(toType)}: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Utils from "./Utils";
|
||||
import {fromHex} from "./lib/Hex";
|
||||
import Utils from "./Utils.mjs";
|
||||
import {fromHex} from "./lib/Hex.mjs";
|
||||
|
||||
/**
|
||||
* The arguments to operations.
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Dish from "./Dish";
|
||||
import Ingredient from "./Ingredient";
|
||||
import Dish from "./Dish.mjs";
|
||||
import Ingredient from "./Ingredient.mjs";
|
||||
|
||||
/**
|
||||
* The Operation specified by the user to be run.
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
*/
|
||||
|
||||
import OperationConfig from "./config/OperationConfig.json";
|
||||
import OperationError from "./errors/OperationError";
|
||||
import Operation from "./Operation";
|
||||
import DishError from "./errors/DishError";
|
||||
import OperationError from "./errors/OperationError.mjs";
|
||||
import Operation from "./Operation.mjs";
|
||||
import DishError from "./errors/DishError.mjs";
|
||||
import log from "loglevel";
|
||||
import { isWorkerEnvironment } from "./Utils.mjs";
|
||||
|
||||
// Cache container for modules
|
||||
let modules = null;
|
||||
|
@ -61,7 +62,7 @@ class Recipe {
|
|||
if (!modules) {
|
||||
// Using Webpack Magic Comments to force the dynamic import to be included in the main chunk
|
||||
// https://webpack.js.org/api/module-methods/
|
||||
modules = await import(/* webpackMode: "eager" */ "./config/modules/OpModules");
|
||||
modules = await import(/* webpackMode: "eager" */ "./config/modules/OpModules.mjs");
|
||||
modules = modules.default;
|
||||
}
|
||||
|
||||
|
@ -200,7 +201,12 @@ class Recipe {
|
|||
|
||||
try {
|
||||
input = await dish.get(op.inputType);
|
||||
log.debug("Executing operation");
|
||||
log.debug(`Executing operation '${op.name}'`);
|
||||
|
||||
if (isWorkerEnvironment()) {
|
||||
self.sendStatusMessage(`Baking... (${i+1}/${this.opList.length})`);
|
||||
self.sendProgressMessage(i + 1, this.opList.length);
|
||||
}
|
||||
|
||||
if (op.flowControl) {
|
||||
// Package up the current state
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
*/
|
||||
|
||||
import utf8 from "utf8";
|
||||
import {fromBase64, toBase64} from "./lib/Base64";
|
||||
import {fromHex} from "./lib/Hex";
|
||||
import {fromDecimal} from "./lib/Decimal";
|
||||
import {fromBinary} from "./lib/Binary";
|
||||
|
||||
import {fromBase64, toBase64} from "./lib/Base64.mjs";
|
||||
import {fromHex} from "./lib/Hex.mjs";
|
||||
import {fromDecimal} from "./lib/Decimal.mjs";
|
||||
import {fromBinary} from "./lib/Binary.mjs";
|
||||
|
||||
/**
|
||||
* Utility functions for use in operations, the core framework and the stage.
|
||||
|
@ -95,7 +94,7 @@ class Utils {
|
|||
const paddedBytes = new Array(numBytes);
|
||||
paddedBytes.fill(padByte);
|
||||
|
||||
Array.prototype.map.call(arr, function(b, i) {
|
||||
[...arr].forEach((b, i) => {
|
||||
paddedBytes[i] = b;
|
||||
});
|
||||
|
||||
|
@ -174,10 +173,11 @@ class Utils {
|
|||
* @returns {string}
|
||||
*/
|
||||
static printable(str, preserveWs=false) {
|
||||
if (ENVIRONMENT_IS_WEB() && window.app && !window.app.options.treatAsUtf8) {
|
||||
if (isWebEnvironment() && window.app && !window.app.options.treatAsUtf8) {
|
||||
str = Utils.byteArrayToChars(Utils.strToByteArray(str));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-misleading-character-class
|
||||
const re = /[\0-\x08\x0B-\x0C\x0E-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BB-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uD7FF\uE000-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF]/g;
|
||||
const wsRe = /[\x09-\x10\x0D\u2028\u2029]/g;
|
||||
|
||||
|
@ -201,11 +201,20 @@ class Utils {
|
|||
* Utils.parseEscapedChars("\\n");
|
||||
*/
|
||||
static parseEscapedChars(str) {
|
||||
return str.replace(/(\\)?\\([bfnrtv0'"]|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\})/g, function(m, a, b) {
|
||||
return str.replace(/(\\)?\\([bfnrtv'"]|[0-3][0-7]{2}|[0-7]{1,2}|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\}|\\)/g, function(m, a, b) {
|
||||
if (a === "\\") return "\\"+b;
|
||||
switch (b[0]) {
|
||||
case "\\":
|
||||
return "\\";
|
||||
case "0":
|
||||
return "\0";
|
||||
case "1":
|
||||
case "2":
|
||||
case "3":
|
||||
case "4":
|
||||
case "5":
|
||||
case "6":
|
||||
case "7":
|
||||
return String.fromCharCode(parseInt(b, 8));
|
||||
case "b":
|
||||
return "\b";
|
||||
case "t":
|
||||
|
@ -411,9 +420,9 @@ class Utils {
|
|||
const utf8Str = utf8.encode(str);
|
||||
|
||||
if (str.length !== utf8Str.length) {
|
||||
if (ENVIRONMENT_IS_WORKER()) {
|
||||
if (isWorkerEnvironment()) {
|
||||
self.setOption("attemptHighlight", false);
|
||||
} else if (ENVIRONMENT_IS_WEB()) {
|
||||
} else if (isWebEnvironment()) {
|
||||
window.app.options.attemptHighlight = false;
|
||||
}
|
||||
}
|
||||
|
@ -466,9 +475,9 @@ class Utils {
|
|||
const utf8Str = utf8.encode(str);
|
||||
|
||||
if (str.length !== utf8Str.length) {
|
||||
if (ENVIRONMENT_IS_WORKER()) {
|
||||
if (isWorkerEnvironment()) {
|
||||
self.setOption("attemptHighlight", false);
|
||||
} else if (ENVIRONMENT_IS_WEB()) {
|
||||
} else if (isWebEnvironment()) {
|
||||
window.app.options.attemptHighlight = false;
|
||||
}
|
||||
}
|
||||
|
@ -528,11 +537,10 @@ class Utils {
|
|||
const str = Utils.byteArrayToChars(byteArray);
|
||||
try {
|
||||
const utf8Str = utf8.decode(str);
|
||||
|
||||
if (str.length !== utf8Str.length) {
|
||||
if (ENVIRONMENT_IS_WORKER()) {
|
||||
if (isWorkerEnvironment()) {
|
||||
self.setOption("attemptHighlight", false);
|
||||
} else if (ENVIRONMENT_IS_WEB()) {
|
||||
} else if (isWebEnvironment()) {
|
||||
window.app.options.attemptHighlight = false;
|
||||
}
|
||||
}
|
||||
|
@ -985,7 +993,7 @@ class Utils {
|
|||
/**
|
||||
* Reads a File and returns the data as a Uint8Array.
|
||||
*
|
||||
* @param {File} file
|
||||
* @param {File | for node: array|arrayBuffer|buffer|string} file
|
||||
* @returns {Uint8Array}
|
||||
*
|
||||
* @example
|
||||
|
@ -993,33 +1001,57 @@ class Utils {
|
|||
* await Utils.readFile(new File(["hello"], "test"))
|
||||
*/
|
||||
static readFile(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
const data = new Uint8Array(file.size);
|
||||
let offset = 0;
|
||||
const CHUNK_SIZE = 10485760; // 10MiB
|
||||
|
||||
const seek = function() {
|
||||
if (offset >= file.size) {
|
||||
resolve(data);
|
||||
return;
|
||||
}
|
||||
const slice = file.slice(offset, offset + CHUNK_SIZE);
|
||||
reader.readAsArrayBuffer(slice);
|
||||
};
|
||||
if (isNodeEnvironment()) {
|
||||
return Buffer.from(file).buffer;
|
||||
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
const data = new Uint8Array(file.size);
|
||||
let offset = 0;
|
||||
const CHUNK_SIZE = 10485760; // 10MiB
|
||||
|
||||
const seek = function() {
|
||||
if (offset >= file.size) {
|
||||
resolve(data);
|
||||
return;
|
||||
}
|
||||
const slice = file.slice(offset, offset + CHUNK_SIZE);
|
||||
reader.readAsArrayBuffer(slice);
|
||||
};
|
||||
|
||||
reader.onload = function(e) {
|
||||
data.set(new Uint8Array(reader.result), offset);
|
||||
offset += CHUNK_SIZE;
|
||||
seek();
|
||||
};
|
||||
|
||||
reader.onerror = function(e) {
|
||||
reject(reader.error.message);
|
||||
};
|
||||
|
||||
reader.onload = function(e) {
|
||||
data.set(new Uint8Array(reader.result), offset);
|
||||
offset += CHUNK_SIZE;
|
||||
seek();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
reader.onerror = function(e) {
|
||||
reject(reader.error.message);
|
||||
};
|
||||
/**
|
||||
* Synchronously read the raw data from a File object.
|
||||
*
|
||||
* Only works in the Node environment
|
||||
*
|
||||
* @param {File} file - a File shim object (see src/node/File.mjs)
|
||||
* @returns {ArrayBuffer} the data from the file in an ArrayBuffer
|
||||
* @throws {TypeError} thrown if the method is called from a browser environment
|
||||
*/
|
||||
static readFileSync(file) {
|
||||
if (!isNodeEnvironment()) {
|
||||
throw new TypeError("Browser environment cannot support readFileSync");
|
||||
}
|
||||
|
||||
seek();
|
||||
});
|
||||
const arrayBuffer = Uint8Array.from(file.data);
|
||||
return arrayBuffer.buffer;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1121,6 +1153,30 @@ class Utils {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the code is running in a Node.js environment
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isNodeEnvironment() {
|
||||
return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the code is running in a web environment
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isWebEnvironment() {
|
||||
return typeof window === "object";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the code is running in a worker
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isWorkerEnvironment() {
|
||||
return typeof importScripts === "function";
|
||||
}
|
||||
|
||||
export default Utils;
|
||||
|
||||
|
||||
|
@ -1139,7 +1195,7 @@ export default Utils;
|
|||
Array.prototype.unique = function() {
|
||||
const u = {}, a = [];
|
||||
for (let i = 0, l = this.length; i < l; i++) {
|
||||
if (u.hasOwnProperty(this[i])) {
|
||||
if (Object.prototype.hasOwnProperty.call(u, this[i])) {
|
||||
continue;
|
||||
}
|
||||
a.push(this[i]);
|
||||
|
@ -1238,12 +1294,13 @@ String.prototype.count = function(chr) {
|
|||
* @param {string} msg
|
||||
*/
|
||||
export function sendStatusMessage(msg) {
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
if (isWorkerEnvironment())
|
||||
self.sendStatusMessage(msg);
|
||||
else if (ENVIRONMENT_IS_WEB())
|
||||
else if (isWebEnvironment())
|
||||
app.alert(msg, 10000);
|
||||
else if (ENVIRONMENT_IS_NODE())
|
||||
log.debug(msg);
|
||||
else if (isNodeEnvironment())
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(msg);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -198,8 +198,8 @@
|
|||
"Remove null bytes",
|
||||
"To Upper case",
|
||||
"To Lower case",
|
||||
"To Case Insensitive Regex",
|
||||
"From Case Insensitive Regex",
|
||||
"To Case Insensitive Regex",
|
||||
"From Case Insensitive Regex",
|
||||
"Add line numbers",
|
||||
"Remove line numbers",
|
||||
"To Table",
|
||||
|
@ -303,6 +303,8 @@
|
|||
"Snefru",
|
||||
"BLAKE2b",
|
||||
"BLAKE2s",
|
||||
"GOST hash",
|
||||
"Streebog",
|
||||
"SSDEEP",
|
||||
"CTPH",
|
||||
"Compare SSDEEP hashes",
|
||||
|
@ -317,6 +319,7 @@
|
|||
"Fletcher-32 Checksum",
|
||||
"Fletcher-64 Checksum",
|
||||
"Adler-32 Checksum",
|
||||
"CRC-8 Checksum",
|
||||
"CRC-16 Checksum",
|
||||
"CRC-32 Checksum",
|
||||
"TCP/IP Checksum"
|
||||
|
@ -385,6 +388,9 @@
|
|||
"Contain Image",
|
||||
"Cover Image",
|
||||
"Image Hue/Saturation/Lightness",
|
||||
"Sharpen Image",
|
||||
"Convert Image Format",
|
||||
"Add Text To Image",
|
||||
"Hex Density chart",
|
||||
"Scatter chart",
|
||||
"Series chart",
|
||||
|
@ -396,6 +402,7 @@
|
|||
"ops": [
|
||||
"Entropy",
|
||||
"Frequency distribution",
|
||||
"Index of Coincidence",
|
||||
"Chi Square",
|
||||
"Disassemble x86",
|
||||
"Pseudo-Random Number Generator",
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
import path from "path";
|
||||
import fs from "fs";
|
||||
import process from "process";
|
||||
import * as Ops from "../../operations/index";
|
||||
import * as Ops from "../../operations/index.mjs";
|
||||
|
||||
const dir = path.join(process.cwd() + "/src/core/config/");
|
||||
if (!fs.existsSync(dir)) {
|
||||
|
@ -45,11 +45,11 @@ for (const opObj in Ops) {
|
|||
args: op.args
|
||||
};
|
||||
|
||||
if (op.hasOwnProperty("patterns")) {
|
||||
if ("patterns" in op) {
|
||||
operationConfig[op.name].patterns = op.patterns;
|
||||
}
|
||||
|
||||
if (!modules.hasOwnProperty(op.module))
|
||||
if (!(op.module in modules))
|
||||
modules[op.module] = {};
|
||||
modules[op.module][op.name] = opObj;
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ for (const module in modules) {
|
|||
|
||||
for (const opName in modules[module]) {
|
||||
const objName = modules[module][opName];
|
||||
code += `import ${objName} from "../../operations/${objName}";\n`;
|
||||
code += `import ${objName} from "../../operations/${objName}.mjs";\n`;
|
||||
}
|
||||
|
||||
code += `
|
||||
|
@ -124,7 +124,7 @@ let opModulesCode = `/**
|
|||
`;
|
||||
|
||||
for (const module in modules) {
|
||||
opModulesCode += `import ${module}Module from "./${module}";\n`;
|
||||
opModulesCode += `import ${module}Module from "./${module}.mjs";\n`;
|
||||
}
|
||||
|
||||
opModulesCode += `
|
||||
|
|
|
@ -39,7 +39,7 @@ let code = `/**
|
|||
`;
|
||||
|
||||
opObjs.forEach(obj => {
|
||||
code += `import ${obj} from "./${obj}";\n`;
|
||||
code += `import ${obj} from "./${obj}.mjs";\n`;
|
||||
});
|
||||
|
||||
code += `
|
||||
|
|
|
@ -13,7 +13,7 @@ import colors from "colors";
|
|||
import process from "process";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import EscapeString from "../../operations/EscapeString";
|
||||
import EscapeString from "../../operations/EscapeString.mjs";
|
||||
|
||||
|
||||
const dir = path.join(process.cwd() + "/src/core/operations/");
|
||||
|
@ -130,8 +130,8 @@ prompt.get(schema, (err, result) => {
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* ${result.opName} operation
|
||||
|
|
39
src/core/dishTypes/DishBigNumber.mjs
Normal file
39
src/core/dishTypes/DishBigNumber.mjs
Normal file
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* @author d98762625 [d98762625@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import DishType from "./DishType.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
/**
|
||||
* translation methods for BigNumber Dishes
|
||||
*/
|
||||
class DishBigNumber extends DishType {
|
||||
|
||||
/**
|
||||
* convert the given value to a ArrayBuffer
|
||||
* @param {BigNumber} value
|
||||
*/
|
||||
static toArrayBuffer() {
|
||||
DishBigNumber.checkForValue(this.value);
|
||||
this.value = BigNumber.isBigNumber(this.value) ? Utils.strToArrayBuffer(this.value.toFixed()) : new ArrayBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert the given value from a ArrayBuffer
|
||||
* @param {boolean} notUTF8
|
||||
*/
|
||||
static fromArrayBuffer(notUTF8) {
|
||||
DishBigNumber.checkForValue(this.value);
|
||||
try {
|
||||
this.value = new BigNumber(Utils.arrayBufferToStr(this.value, !notUTF8));
|
||||
} catch (err) {
|
||||
this.value = new BigNumber(NaN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DishBigNumber;
|
31
src/core/dishTypes/DishByteArray.mjs
Normal file
31
src/core/dishTypes/DishByteArray.mjs
Normal file
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @author d98762625 [d98762625@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import DishType from "./DishType.mjs";
|
||||
|
||||
/**
|
||||
* Translation methods for ArrayBuffer Dishes
|
||||
*/
|
||||
class DishByteArray extends DishType {
|
||||
|
||||
/**
|
||||
* convert the given value to a ArrayBuffer
|
||||
*/
|
||||
static toArrayBuffer() {
|
||||
DishByteArray.checkForValue(this.value);
|
||||
this.value = new Uint8Array(this.value).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert the given value from a ArrayBuffer
|
||||
*/
|
||||
static fromArrayBuffer() {
|
||||
DishByteArray.checkForValue(this.value);
|
||||
this.value = Array.prototype.slice.call(new Uint8Array(this.value));
|
||||
}
|
||||
}
|
||||
|
||||
export default DishByteArray;
|
42
src/core/dishTypes/DishFile.mjs
Normal file
42
src/core/dishTypes/DishFile.mjs
Normal file
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* @author d98762625 [d98762625@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import DishType from "./DishType.mjs";
|
||||
import Utils, { isNodeEnvironment } from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Translation methods for file Dishes
|
||||
*/
|
||||
class DishFile extends DishType {
|
||||
|
||||
/**
|
||||
* convert the given value to an ArrayBuffer
|
||||
* @param {File} value
|
||||
*/
|
||||
static toArrayBuffer() {
|
||||
DishFile.checkForValue(this.value);
|
||||
if (isNodeEnvironment()) {
|
||||
this.value = Utils.readFileSync(this.value);
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
Utils.readFile(this.value)
|
||||
.then(v => this.value = v.buffer)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* convert the given value from an ArrayBuffer
|
||||
*/
|
||||
static fromArrayBuffer() {
|
||||
DishFile.checkForValue(this.value);
|
||||
this.value = new File(this.value, "unknown");
|
||||
}
|
||||
}
|
||||
|
||||
export default DishFile;
|
26
src/core/dishTypes/DishHTML.mjs
Normal file
26
src/core/dishTypes/DishHTML.mjs
Normal file
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* @author d98762625 [d98762625@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import DishString from "./DishString.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Translation methods for HTML Dishes
|
||||
*/
|
||||
class DishHTML extends DishString {
|
||||
|
||||
/**
|
||||
* convert the given value to a ArrayBuffer
|
||||
* @param {String} value
|
||||
*/
|
||||
static toArrayBuffer() {
|
||||
DishHTML.checkForValue(this.value);
|
||||
this.value = this.value ? Utils.strToArrayBuffer(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : new ArrayBuffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default DishHTML;
|
33
src/core/dishTypes/DishJSON.mjs
Normal file
33
src/core/dishTypes/DishJSON.mjs
Normal file
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* @author d98762625 [d98762625@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import DishType from "./DishType.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Translation methods for JSON dishes
|
||||
*/
|
||||
class DishJSON extends DishType {
|
||||
|
||||
/**
|
||||
* convert the given value to a ArrayBuffer
|
||||
*/
|
||||
static toArrayBuffer() {
|
||||
DishJSON.checkForValue(this.value);
|
||||
this.value = this.value ? Utils.strToArrayBuffer(JSON.stringify(this.value, null, 4)) : new ArrayBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert the given value from a ArrayBuffer
|
||||
* @param {boolean} notUTF8
|
||||
*/
|
||||
static fromArrayBuffer(notUTF8) {
|
||||
DishJSON.checkForValue(this.value);
|
||||
this.value = JSON.parse(Utils.arrayBufferToStr(this.value, !notUTF8));
|
||||
}
|
||||
}
|
||||
|
||||
export default DishJSON;
|
58
src/core/dishTypes/DishListFile.mjs
Normal file
58
src/core/dishTypes/DishListFile.mjs
Normal file
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* @author d98762625 [d98762625@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import DishType from "./DishType.mjs";
|
||||
import { isNodeEnvironment } from "../Utils.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* Translation methods for ListFile Dishes
|
||||
*/
|
||||
class DishListFile extends DishType {
|
||||
|
||||
/**
|
||||
* convert the given value to a ArrayBuffer
|
||||
*/
|
||||
static toArrayBuffer() {
|
||||
DishListFile.checkForValue(this.value);
|
||||
|
||||
if (isNodeEnvironment()) {
|
||||
this.value = this.value.map(file => Uint8Array.from(file.data));
|
||||
}
|
||||
this.value = DishListFile.concatenateTypedArrays(...this.value).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert the given value from a ArrayBuffer
|
||||
*/
|
||||
static fromArrayBuffer() {
|
||||
DishListFile.checkForValue(this.value);
|
||||
this.value = [new File(this.value, "unknown")];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Concatenates a list of Uint8Arrays together
|
||||
*
|
||||
* @param {Uint8Array[]} arrays
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
static concatenateTypedArrays(...arrays) {
|
||||
let totalLength = 0;
|
||||
for (const arr of arrays) {
|
||||
totalLength += arr.length;
|
||||
}
|
||||
const result = new Uint8Array(totalLength);
|
||||
let offset = 0;
|
||||
for (const arr of arrays) {
|
||||
result.set(arr, offset);
|
||||
offset += arr.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export default DishListFile;
|
34
src/core/dishTypes/DishNumber.mjs
Normal file
34
src/core/dishTypes/DishNumber.mjs
Normal file
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* @author d98762625 [d98762625@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
import DishType from "./DishType.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Translation methods for number dishes
|
||||
*/
|
||||
class DishNumber extends DishType {
|
||||
|
||||
/**
|
||||
* convert the given value to a ArrayBuffer
|
||||
*/
|
||||
static toArrayBuffer() {
|
||||
DishNumber.checkForValue(this.value);
|
||||
this.value = typeof this.value === "number" ? Utils.strToArrayBuffer(this.value.toString()) : new ArrayBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert the given value from a ArrayBuffer
|
||||
* @param {boolean} notUTF8
|
||||
*/
|
||||
static fromArrayBuffer(notUTF8) {
|
||||
DishNumber.checkForValue(this.value);
|
||||
this.value = this.value ? parseFloat(Utils.arrayBufferToStr(this.value, !notUTF8)) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
export default DishNumber;
|
34
src/core/dishTypes/DishString.mjs
Normal file
34
src/core/dishTypes/DishString.mjs
Normal file
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* @author d98762625 [d98762625@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
import DishType from "./DishType.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Translation methods for string dishes
|
||||
*/
|
||||
class DishString extends DishType {
|
||||
|
||||
/**
|
||||
* convert the given value to a ArrayBuffer
|
||||
*/
|
||||
static toArrayBuffer() {
|
||||
DishString.checkForValue(this.value);
|
||||
this.value = this.value ? Utils.strToArrayBuffer(this.value) : new ArrayBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert the given value from a ArrayBuffer
|
||||
* @param {boolean} notUTF8
|
||||
*/
|
||||
static fromArrayBuffer(notUTF8) {
|
||||
DishString.checkForValue(this.value);
|
||||
this.value = this.value ? Utils.arrayBufferToStr(this.value, !notUTF8) : "";
|
||||
}
|
||||
}
|
||||
|
||||
export default DishString;
|
39
src/core/dishTypes/DishType.mjs
Normal file
39
src/core/dishTypes/DishType.mjs
Normal file
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* @author d98762625 [d98762625@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Abstract class for dish translation methods
|
||||
*/
|
||||
class DishType {
|
||||
|
||||
/**
|
||||
* Warn translations dont work without value from bind
|
||||
*/
|
||||
static checkForValue(value) {
|
||||
if (value === undefined) {
|
||||
throw new Error("only use translation methods with .bind");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* convert the given value to a ArrayBuffer
|
||||
* @param {*} value
|
||||
*/
|
||||
static toArrayBuffer() {
|
||||
throw new Error("toArrayBuffer has not been implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* convert the given value from a ArrayBuffer
|
||||
* @param {boolean} notUTF8
|
||||
*/
|
||||
static fromArrayBuffer(notUTF8=undefined) {
|
||||
throw new Error("fromArrayBuffer has not been implemented");
|
||||
}
|
||||
}
|
||||
|
||||
export default DishType;
|
26
src/core/dishTypes/index.mjs
Normal file
26
src/core/dishTypes/index.mjs
Normal file
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* @author d98762625 [d98762625@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
import DishByteArray from "./DishByteArray.mjs";
|
||||
import DishBigNumber from "./DishBigNumber.mjs";
|
||||
import DishFile from "./DishFile.mjs";
|
||||
import DishHTML from "./DishHTML.mjs";
|
||||
import DishJSON from "./DishJSON.mjs";
|
||||
import DishListFile from "./DishListFile.mjs";
|
||||
import DishNumber from "./DishNumber.mjs";
|
||||
import DishString from "./DishString.mjs";
|
||||
|
||||
export {
|
||||
DishByteArray,
|
||||
DishBigNumber,
|
||||
DishFile,
|
||||
DishHTML,
|
||||
DishJSON,
|
||||
DishListFile,
|
||||
DishNumber,
|
||||
DishString,
|
||||
};
|
25
src/core/errors/ExcludedOperationError.mjs
Normal file
25
src/core/errors/ExcludedOperationError.mjs
Normal file
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* Custom error type for handling operation that isnt included in node.js API
|
||||
*
|
||||
* @author d98762625 [d98762625@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
class ExcludedOperationError extends Error {
|
||||
/**
|
||||
* Standard error constructor. Adds no new behaviour.
|
||||
*
|
||||
* @param args - Standard error args
|
||||
*/
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this.type = "ExcludedOperationError";
|
||||
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, ExcludedOperationError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ExcludedOperationError;
|
|
@ -5,7 +5,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Utils from "../Utils";
|
||||
import Utils from "../Utils.mjs";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Utils from "../Utils";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -63,7 +63,7 @@ export function toBase64(data, alphabet="A-Za-z0-9+/=") {
|
|||
/**
|
||||
* UnBase64's the input string using the given alphabet, returning a byte array.
|
||||
*
|
||||
* @param {byteArray} data
|
||||
* @param {string} data
|
||||
* @param {string} [alphabet="A-Za-z0-9+/="]
|
||||
* @param {string} [returnType="string"] - Either "string" or "byteArray"
|
||||
* @param {boolean} [removeNonAlphChars=true]
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Utils from "../Utils";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
/**
|
||||
* Runs bitwise operations across the input data.
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {byteArray|Uint8Array} input
|
||||
* @param {byteArray} key
|
||||
* @param {function} func - The bitwise calculation to carry out
|
||||
* @param {boolean} nullPreserving
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Utils from "../Utils";
|
||||
import {Rotor, Plugboard, a2i, i2a} from "./Enigma";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import {Rotor, Plugboard, a2i, i2a} from "./Enigma.mjs";
|
||||
|
||||
/**
|
||||
* Convenience/optimisation subclass of Rotor
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import OperationError from "../errors/OperationError";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* @constant
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import OperationError from "../errors/OperationError";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import CryptoJS from "crypto-js";
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import geohash from "ngeohash";
|
||||
import geodesy from "geodesy";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Co-ordinate formats
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Utils from "../Utils";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Utils from "../Utils";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Provided default Enigma rotor set.
|
||||
|
@ -184,10 +184,10 @@ class PairMapBase {
|
|||
// self-stecker
|
||||
return;
|
||||
}
|
||||
if (this.map.hasOwnProperty(a)) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.map, a)) {
|
||||
throw new OperationError(`${name} connects ${pair[0]} more than once`);
|
||||
}
|
||||
if (this.map.hasOwnProperty(b)) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.map, b)) {
|
||||
throw new OperationError(`${name} connects ${pair[1]} more than once`);
|
||||
}
|
||||
this.map[a] = b;
|
||||
|
@ -203,7 +203,7 @@ class PairMapBase {
|
|||
* @returns {number}
|
||||
*/
|
||||
transform(c) {
|
||||
if (!this.map.hasOwnProperty(c)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(this.map, c)) {
|
||||
return c;
|
||||
}
|
||||
return this.map[c];
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* @license Apache-2.0
|
||||
*
|
||||
*/
|
||||
import Stream from "./Stream";
|
||||
import Stream from "./Stream.mjs";
|
||||
|
||||
/**
|
||||
* A categorised table of file types, including signatures to identify them and functions
|
||||
|
@ -170,7 +170,7 @@ export const FILE_SIGNATURES = {
|
|||
mime: "image/vnd.adobe.photoshop",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x38,
|
||||
0: 0x38, // 8BPS
|
||||
1: 0x42,
|
||||
2: 0x50,
|
||||
3: 0x53,
|
||||
|
@ -185,6 +185,28 @@ export const FILE_SIGNATURES = {
|
|||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Photoshop Large Document",
|
||||
extension: "psb",
|
||||
mime: "application/x-photoshop",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x38, // 8BPS
|
||||
1: 0x42,
|
||||
2: 0x50,
|
||||
3: 0x53,
|
||||
4: 0x0,
|
||||
5: 0x2,
|
||||
6: 0x0,
|
||||
7: 0x0,
|
||||
8: 0x0,
|
||||
9: 0x0,
|
||||
10: 0x0,
|
||||
11: 0x0,
|
||||
12: 0x0
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Paint Shop Pro image",
|
||||
extension: "psp",
|
||||
|
@ -233,10 +255,114 @@ export const FILE_SIGNATURES = {
|
|||
5: 0x0,
|
||||
6: [0x10, 0x20, 0x30, 0x40, 0x80],
|
||||
7: [0x10, 0x20, 0x30, 0x40, 0x80],
|
||||
9: 0x00,
|
||||
9: 0x0,
|
||||
10: [0x0, 0x1]
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Radiance High Dynamic Range image",
|
||||
extension: "hdr",
|
||||
mime: "image/vnd.radiance",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x23, // #?RADIANCE
|
||||
1: 0x3f,
|
||||
2: 0x52,
|
||||
3: 0x41,
|
||||
4: 0x44,
|
||||
5: 0x49,
|
||||
6: 0x41,
|
||||
7: 0x4e,
|
||||
8: 0x43,
|
||||
9: 0x45,
|
||||
10: 0x0a
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Sony ARW image",
|
||||
extension: "arw",
|
||||
mime: "image/x-raw",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x05,
|
||||
1: 0x0,
|
||||
2: 0x0,
|
||||
3: 0x0,
|
||||
4: 0x41,
|
||||
5: 0x57,
|
||||
6: 0x31,
|
||||
7: 0x2e
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Fujifilm Raw Image",
|
||||
extension: "raf",
|
||||
mime: "image/x-raw",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x46, // FUJIFILMCCD-RAW
|
||||
1: 0x55,
|
||||
2: 0x4a,
|
||||
3: 0x49,
|
||||
4: 0x46,
|
||||
5: 0x49,
|
||||
6: 0x4c,
|
||||
7: 0x4d,
|
||||
8: 0x43,
|
||||
9: 0x43,
|
||||
10: 0x44,
|
||||
11: 0x2d,
|
||||
12: 0x52,
|
||||
13: 0x41,
|
||||
14: 0x57
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Minolta RAW image",
|
||||
extension: "mrw",
|
||||
mime: "image/x-raw",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x0,
|
||||
1: 0x4d, // MRM
|
||||
2: 0x52,
|
||||
3: 0x4d
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Adobe Bridge Thumbnail Cache",
|
||||
extension: "bct",
|
||||
mime: "application/octet-stream",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x6c,
|
||||
1: 0x6e,
|
||||
2: 0x62,
|
||||
3: 0x74,
|
||||
4: 0x02,
|
||||
5: 0x0,
|
||||
6: 0x0,
|
||||
7: 0x0
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Microsoft Document Imaging",
|
||||
extension: "mdi",
|
||||
mime: "image/vnd.ms-modi",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x45,
|
||||
1: 0x50,
|
||||
2: 0x2a,
|
||||
3: 0x00
|
||||
},
|
||||
extractor: null
|
||||
}
|
||||
],
|
||||
"Video": [
|
||||
|
@ -534,6 +660,97 @@ export const FILE_SIGNATURES = {
|
|||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Audacity",
|
||||
extension: "au",
|
||||
mime: "audio/x-au",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x64, // dns.
|
||||
1: 0x6e,
|
||||
2: 0x73,
|
||||
3: 0x2e,
|
||||
|
||||
24: 0x41, // AudacityBlockFile
|
||||
25: 0x75,
|
||||
26: 0x64,
|
||||
27: 0x61,
|
||||
28: 0x63,
|
||||
29: 0x69,
|
||||
30: 0x74,
|
||||
31: 0x79,
|
||||
32: 0x42,
|
||||
33: 0x6c,
|
||||
34: 0x6f,
|
||||
35: 0x63,
|
||||
36: 0x6b,
|
||||
37: 0x46,
|
||||
38: 0x69,
|
||||
39: 0x6c,
|
||||
40: 0x65
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Audacity Block",
|
||||
extension: "auf",
|
||||
mime: "application/octet-stream",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x41, // AudacityBlockFile
|
||||
1: 0x75,
|
||||
2: 0x64,
|
||||
3: 0x61,
|
||||
4: 0x63,
|
||||
5: 0x69,
|
||||
6: 0x74,
|
||||
7: 0x79,
|
||||
8: 0x42,
|
||||
9: 0x6c,
|
||||
10: 0x6f,
|
||||
11: 0x63,
|
||||
12: 0x6b,
|
||||
13: 0x46,
|
||||
14: 0x69,
|
||||
15: 0x6c,
|
||||
16: 0x65
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Audio Interchange File",
|
||||
extension: "aif",
|
||||
mime: "audio/x-aiff",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x46, // FORM
|
||||
1: 0x4f,
|
||||
2: 0x52,
|
||||
3: 0x4d,
|
||||
8: 0x41, // AIFF
|
||||
9: 0x49,
|
||||
10: 0x46,
|
||||
11: 0x46
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Audio Interchange File (compressed)",
|
||||
extension: "aifc",
|
||||
mime: "audio/x-aifc",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x46, // FORM
|
||||
1: 0x4f,
|
||||
2: 0x52,
|
||||
3: 0x4d,
|
||||
8: 0x41, // AIFC
|
||||
9: 0x49,
|
||||
10: 0x46,
|
||||
11: 0x43
|
||||
},
|
||||
extractor: null
|
||||
}
|
||||
],
|
||||
"Documents": [
|
||||
{
|
||||
|
@ -898,6 +1115,110 @@ export const FILE_SIGNATURES = {
|
|||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "ARJ Archive",
|
||||
extension: "arj",
|
||||
mime: "application/x-arj-compressed",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x60,
|
||||
1: 0xea,
|
||||
8: [0x0, 0x10, 0x14],
|
||||
9: 0x0,
|
||||
10: 0x2
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "WinAce Archive",
|
||||
extension: "ace",
|
||||
mime: "application/x-ace-compressed",
|
||||
description: "",
|
||||
signature: {
|
||||
7: 0x2a, // **ACE**
|
||||
8: 0x2a,
|
||||
9: 0x41,
|
||||
10: 0x43,
|
||||
11: 0x45,
|
||||
12: 0x2a,
|
||||
13: 0x2a
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Macintosh BinHex Encoded File",
|
||||
extension: "hqx",
|
||||
mime: "application/mac-binhex",
|
||||
description: "",
|
||||
signature: {
|
||||
11: 0x6d, // must be converted with BinHex
|
||||
12: 0x75,
|
||||
13: 0x73,
|
||||
14: 0x74,
|
||||
15: 0x20,
|
||||
16: 0x62,
|
||||
17: 0x65,
|
||||
18: 0x20,
|
||||
19: 0x63,
|
||||
20: 0x6f,
|
||||
21: 0x6e,
|
||||
22: 0x76,
|
||||
23: 0x65,
|
||||
24: 0x72,
|
||||
25: 0x74,
|
||||
26: 0x65,
|
||||
27: 0x64,
|
||||
28: 0x20,
|
||||
29: 0x77,
|
||||
30: 0x69,
|
||||
31: 0x74,
|
||||
32: 0x68,
|
||||
33: 0x20,
|
||||
34: 0x42,
|
||||
35: 0x69,
|
||||
36: 0x6e,
|
||||
37: 0x48,
|
||||
38: 0x65,
|
||||
39: 0x78
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "ALZip Archive",
|
||||
extension: "alz",
|
||||
mime: "application/octet-stream",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x41, // ALZ
|
||||
1: 0x4c,
|
||||
2: 0x5a,
|
||||
3: 0x01,
|
||||
4: 0x0a,
|
||||
5: 0x0,
|
||||
6: 0x0,
|
||||
7: 0x0
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "KGB Compressed Archive",
|
||||
extension: "kgb",
|
||||
mime: "application/x-kgb-compressed",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x4b, // KGB_arch -
|
||||
1: 0x47,
|
||||
2: 0x42,
|
||||
3: 0x5f,
|
||||
4: 0x61,
|
||||
5: 0x72,
|
||||
6: 0x63,
|
||||
7: 0x68,
|
||||
8: 0x20,
|
||||
9: 0x2d
|
||||
},
|
||||
extractor: null
|
||||
}
|
||||
],
|
||||
"Miscellaneous": [
|
||||
{
|
||||
|
@ -1041,6 +1362,43 @@ export const FILE_SIGNATURES = {
|
|||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "BitTorrent link",
|
||||
extension: "torrent",
|
||||
mime: "application/x-bittorrent",
|
||||
description: "",
|
||||
signature: [
|
||||
{
|
||||
0: 0x64, // d8:announce##:
|
||||
1: 0x38,
|
||||
2: 0x3a,
|
||||
3: 0x61,
|
||||
4: 0x6e,
|
||||
5: 0x6e,
|
||||
6: 0x6f,
|
||||
7: 0x75,
|
||||
8: 0x6e,
|
||||
9: 0x63,
|
||||
10: 0x65,
|
||||
11: 0x23,
|
||||
12: 0x23,
|
||||
13: 0x3a
|
||||
},
|
||||
{
|
||||
0: 0x64, // d4:infod
|
||||
1: 0x34,
|
||||
2: 0x3a,
|
||||
3: 0x69,
|
||||
4: 0x6e,
|
||||
5: 0x66,
|
||||
6: 0x6f,
|
||||
7: 0x64,
|
||||
8: [0x34, 0x35, 0x36],
|
||||
9: 0x3a
|
||||
}
|
||||
],
|
||||
extractor: null
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
* @license Apache-2.0
|
||||
*
|
||||
*/
|
||||
import {FILE_SIGNATURES} from "./FileSignatures";
|
||||
import {sendStatusMessage} from "../Utils";
|
||||
import {FILE_SIGNATURES} from "./FileSignatures.mjs";
|
||||
import {sendStatusMessage} from "../Utils.mjs";
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Utils from "../Utils";
|
||||
import CryptoApi from "crypto-api/src/crypto-api";
|
||||
import Utils from "../Utils.mjs";
|
||||
import CryptoApi from "crypto-api/src/crypto-api.mjs";
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Utils from "../Utils";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* Convert a byte array into a hex string.
|
||||
*
|
||||
* @param {Uint8Array|byteArray} data
|
||||
* @param {byteArray|Uint8Array|ArrayBuffer} data
|
||||
* @param {string} [delim=" "]
|
||||
* @param {number} [padding=2]
|
||||
* @returns {string}
|
||||
|
@ -26,6 +26,7 @@ import Utils from "../Utils";
|
|||
*/
|
||||
export function toHex(data, delim=" ", padding=2) {
|
||||
if (!data) return "";
|
||||
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
|
||||
|
||||
let output = "";
|
||||
|
||||
|
@ -47,7 +48,7 @@ export function toHex(data, delim=" ", padding=2) {
|
|||
/**
|
||||
* Convert a byte array into a hex string as efficiently as possible with no options.
|
||||
*
|
||||
* @param {byteArray} data
|
||||
* @param {byteArray|Uint8Array|ArrayBuffer} data
|
||||
* @returns {string}
|
||||
*
|
||||
* @example
|
||||
|
@ -56,6 +57,7 @@ export function toHex(data, delim=" ", padding=2) {
|
|||
*/
|
||||
export function toHexFast(data) {
|
||||
if (!data) return "";
|
||||
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
|
||||
|
||||
const output = [];
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Utils from "../Utils";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Parses an IPv4 CIDR range (e.g. 192.168.0.0/24) and displays information about it.
|
||||
|
|
251
src/core/lib/ImageManipulation.mjs
Normal file
251
src/core/lib/ImageManipulation.mjs
Normal file
|
@ -0,0 +1,251 @@
|
|||
/**
|
||||
* Image manipulation resources
|
||||
*
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Gaussian blurs an image.
|
||||
*
|
||||
* @param {jimp} input
|
||||
* @param {number} radius
|
||||
* @param {boolean} fast
|
||||
* @returns {jimp}
|
||||
*/
|
||||
export function gaussianBlur (input, radius) {
|
||||
try {
|
||||
// From http://blog.ivank.net/fastest-gaussian-blur.html
|
||||
const boxes = boxesForGauss(radius, 3);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
input = boxBlur(input, (boxes[i] - 1) / 2);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error blurring image. (${err})`);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} radius
|
||||
* @param {number} numBoxes
|
||||
* @returns {Array}
|
||||
*/
|
||||
function boxesForGauss(radius, numBoxes) {
|
||||
const idealWidth = Math.sqrt((12 * radius * radius / numBoxes) + 1);
|
||||
|
||||
let wl = Math.floor(idealWidth);
|
||||
|
||||
if (wl % 2 === 0) {
|
||||
wl--;
|
||||
}
|
||||
|
||||
const wu = wl + 2;
|
||||
|
||||
const mIdeal = (12 * radius * radius - numBoxes * wl * wl - 4 * numBoxes * wl - 3 * numBoxes) / (-4 * wl - 4);
|
||||
const m = Math.round(mIdeal);
|
||||
|
||||
const sizes = [];
|
||||
for (let i = 0; i < numBoxes; i++) {
|
||||
sizes.push(i < m ? wl : wu);
|
||||
}
|
||||
return sizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a box blur effect to the image
|
||||
*
|
||||
* @param {jimp} source
|
||||
* @param {number} radius
|
||||
* @returns {jimp}
|
||||
*/
|
||||
function boxBlur (source, radius) {
|
||||
const width = source.bitmap.width;
|
||||
const height = source.bitmap.height;
|
||||
let output = source.clone();
|
||||
output = boxBlurH(source, output, width, height, radius);
|
||||
source = boxBlurV(output, source, width, height, radius);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the horizontal blur
|
||||
*
|
||||
* @param {jimp} source
|
||||
* @param {jimp} output
|
||||
* @param {number} width
|
||||
* @param {number} height
|
||||
* @param {number} radius
|
||||
* @returns {jimp}
|
||||
*/
|
||||
function boxBlurH (source, output, width, height, radius) {
|
||||
const iarr = 1 / (radius + radius + 1);
|
||||
for (let i = 0; i < height; i++) {
|
||||
let ti = 0,
|
||||
li = ti,
|
||||
ri = ti + radius;
|
||||
const idx = source.getPixelIndex(ti, i);
|
||||
const firstValRed = source.bitmap.data[idx],
|
||||
firstValGreen = source.bitmap.data[idx + 1],
|
||||
firstValBlue = source.bitmap.data[idx + 2],
|
||||
firstValAlpha = source.bitmap.data[idx + 3];
|
||||
|
||||
const lastIdx = source.getPixelIndex(width - 1, i),
|
||||
lastValRed = source.bitmap.data[lastIdx],
|
||||
lastValGreen = source.bitmap.data[lastIdx + 1],
|
||||
lastValBlue = source.bitmap.data[lastIdx + 2],
|
||||
lastValAlpha = source.bitmap.data[lastIdx + 3];
|
||||
|
||||
let red = (radius + 1) * firstValRed;
|
||||
let green = (radius + 1) * firstValGreen;
|
||||
let blue = (radius + 1) * firstValBlue;
|
||||
let alpha = (radius + 1) * firstValAlpha;
|
||||
|
||||
for (let j = 0; j < radius; j++) {
|
||||
const jIdx = source.getPixelIndex(ti + j, i);
|
||||
red += source.bitmap.data[jIdx];
|
||||
green += source.bitmap.data[jIdx + 1];
|
||||
blue += source.bitmap.data[jIdx + 2];
|
||||
alpha += source.bitmap.data[jIdx + 3];
|
||||
}
|
||||
|
||||
for (let j = 0; j <= radius; j++) {
|
||||
const jIdx = source.getPixelIndex(ri++, i);
|
||||
red += source.bitmap.data[jIdx] - firstValRed;
|
||||
green += source.bitmap.data[jIdx + 1] - firstValGreen;
|
||||
blue += source.bitmap.data[jIdx + 2] - firstValBlue;
|
||||
alpha += source.bitmap.data[jIdx + 3] - firstValAlpha;
|
||||
|
||||
const tiIdx = source.getPixelIndex(ti++, i);
|
||||
output.bitmap.data[tiIdx] = Math.round(red * iarr);
|
||||
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
|
||||
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
|
||||
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
|
||||
}
|
||||
|
||||
for (let j = radius + 1; j < width - radius; j++) {
|
||||
const riIdx = source.getPixelIndex(ri++, i);
|
||||
const liIdx = source.getPixelIndex(li++, i);
|
||||
red += source.bitmap.data[riIdx] - source.bitmap.data[liIdx];
|
||||
green += source.bitmap.data[riIdx + 1] - source.bitmap.data[liIdx + 1];
|
||||
blue += source.bitmap.data[riIdx + 2] - source.bitmap.data[liIdx + 2];
|
||||
alpha += source.bitmap.data[riIdx + 3] - source.bitmap.data[liIdx + 3];
|
||||
|
||||
const tiIdx = source.getPixelIndex(ti++, i);
|
||||
output.bitmap.data[tiIdx] = Math.round(red * iarr);
|
||||
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
|
||||
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
|
||||
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
|
||||
}
|
||||
|
||||
for (let j = width - radius; j < width; j++) {
|
||||
const liIdx = source.getPixelIndex(li++, i);
|
||||
red += lastValRed - source.bitmap.data[liIdx];
|
||||
green += lastValGreen - source.bitmap.data[liIdx + 1];
|
||||
blue += lastValBlue - source.bitmap.data[liIdx + 2];
|
||||
alpha += lastValAlpha - source.bitmap.data[liIdx + 3];
|
||||
|
||||
const tiIdx = source.getPixelIndex(ti++, i);
|
||||
output.bitmap.data[tiIdx] = Math.round(red * iarr);
|
||||
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
|
||||
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
|
||||
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the vertical blur
|
||||
*
|
||||
* @param {jimp} source
|
||||
* @param {jimp} output
|
||||
* @param {number} width
|
||||
* @param {number} height
|
||||
* @param {number} radius
|
||||
* @returns {jimp}
|
||||
*/
|
||||
function boxBlurV (source, output, width, height, radius) {
|
||||
const iarr = 1 / (radius + radius + 1);
|
||||
for (let i = 0; i < width; i++) {
|
||||
let ti = 0,
|
||||
li = ti,
|
||||
ri = ti + radius;
|
||||
|
||||
const idx = source.getPixelIndex(i, ti);
|
||||
|
||||
const firstValRed = source.bitmap.data[idx],
|
||||
firstValGreen = source.bitmap.data[idx + 1],
|
||||
firstValBlue = source.bitmap.data[idx + 2],
|
||||
firstValAlpha = source.bitmap.data[idx + 3];
|
||||
|
||||
const lastIdx = source.getPixelIndex(i, height - 1),
|
||||
lastValRed = source.bitmap.data[lastIdx],
|
||||
lastValGreen = source.bitmap.data[lastIdx + 1],
|
||||
lastValBlue = source.bitmap.data[lastIdx + 2],
|
||||
lastValAlpha = source.bitmap.data[lastIdx + 3];
|
||||
|
||||
let red = (radius + 1) * firstValRed;
|
||||
let green = (radius + 1) * firstValGreen;
|
||||
let blue = (radius + 1) * firstValBlue;
|
||||
let alpha = (radius + 1) * firstValAlpha;
|
||||
|
||||
for (let j = 0; j < radius; j++) {
|
||||
const jIdx = source.getPixelIndex(i, ti + j);
|
||||
red += source.bitmap.data[jIdx];
|
||||
green += source.bitmap.data[jIdx + 1];
|
||||
blue += source.bitmap.data[jIdx + 2];
|
||||
alpha += source.bitmap.data[jIdx + 3];
|
||||
}
|
||||
|
||||
for (let j = 0; j <= radius; j++) {
|
||||
const riIdx = source.getPixelIndex(i, ri++);
|
||||
red += source.bitmap.data[riIdx] - firstValRed;
|
||||
green += source.bitmap.data[riIdx + 1] - firstValGreen;
|
||||
blue += source.bitmap.data[riIdx + 2] - firstValBlue;
|
||||
alpha += source.bitmap.data[riIdx + 3] - firstValAlpha;
|
||||
|
||||
const tiIdx = source.getPixelIndex(i, ti++);
|
||||
output.bitmap.data[tiIdx] = Math.round(red * iarr);
|
||||
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
|
||||
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
|
||||
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
|
||||
}
|
||||
|
||||
for (let j = radius + 1; j < height - radius; j++) {
|
||||
const riIdx = source.getPixelIndex(i, ri++);
|
||||
const liIdx = source.getPixelIndex(i, li++);
|
||||
red += source.bitmap.data[riIdx] - source.bitmap.data[liIdx];
|
||||
green += source.bitmap.data[riIdx + 1] - source.bitmap.data[liIdx + 1];
|
||||
blue += source.bitmap.data[riIdx + 2] - source.bitmap.data[liIdx + 2];
|
||||
alpha += source.bitmap.data[riIdx + 3] - source.bitmap.data[liIdx + 3];
|
||||
|
||||
const tiIdx = source.getPixelIndex(i, ti++);
|
||||
output.bitmap.data[tiIdx] = Math.round(red * iarr);
|
||||
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
|
||||
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
|
||||
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
|
||||
}
|
||||
|
||||
for (let j = height - radius; j < height; j++) {
|
||||
const liIdx = source.getPixelIndex(i, li++);
|
||||
red += lastValRed - source.bitmap.data[liIdx];
|
||||
green += lastValGreen - source.bitmap.data[liIdx + 1];
|
||||
blue += lastValBlue - source.bitmap.data[liIdx + 2];
|
||||
alpha += lastValAlpha - source.bitmap.data[liIdx + 3];
|
||||
|
||||
const tiIdx = source.getPixelIndex(i, ti++);
|
||||
output.bitmap.data[tiIdx] = Math.round(red * iarr);
|
||||
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
|
||||
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
|
||||
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import OperationConfig from "../config/OperationConfig.json";
|
||||
import Utils from "../Utils";
|
||||
import Recipe from "../Recipe";
|
||||
import Dish from "../Dish";
|
||||
import {detectFileType} from "./FileType";
|
||||
import Utils, { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import Recipe from "../Recipe.mjs";
|
||||
import Dish from "../Dish.mjs";
|
||||
import {detectFileType} from "./FileType.mjs";
|
||||
import chiSquared from "chi-squared";
|
||||
|
||||
/**
|
||||
|
@ -312,6 +312,11 @@ class Magic {
|
|||
return;
|
||||
}
|
||||
|
||||
// If the recipe returned an empty buffer, do not continue
|
||||
if (_buffersEqual(output, new ArrayBuffer())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const magic = new Magic(output, this.opPatterns),
|
||||
speculativeResults = await magic.speculativeExecution(
|
||||
depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful, crib);
|
||||
|
@ -333,7 +338,7 @@ class Magic {
|
|||
}
|
||||
|
||||
// Prune branches that result in unhelpful outputs
|
||||
results = results.filter(r =>
|
||||
const prunedResults = results.filter(r =>
|
||||
(r.useful || r.data.length > 0) && // The operation resulted in ""
|
||||
( // One of the following must be true
|
||||
r.languageScores[0].probability > 0 || // Some kind of language was found
|
||||
|
@ -344,7 +349,7 @@ class Magic {
|
|||
);
|
||||
|
||||
// Return a sorted list of possible recipes along with their properties
|
||||
return results.sort((a, b) => {
|
||||
return prunedResults.sort((a, b) => {
|
||||
// Each option is sorted based on its most likely language (lower is better)
|
||||
let aScore = a.languageScores[0].score,
|
||||
bScore = b.languageScores[0].score;
|
||||
|
@ -390,12 +395,17 @@ class Magic {
|
|||
const dish = new Dish();
|
||||
dish.set(input, Dish.ARRAY_BUFFER);
|
||||
|
||||
if (ENVIRONMENT_IS_WORKER()) self.loadRequiredModules(recipeConfig);
|
||||
if (isWorkerEnvironment()) self.loadRequiredModules(recipeConfig);
|
||||
|
||||
const recipe = new Recipe(recipeConfig);
|
||||
try {
|
||||
await recipe.execute(dish);
|
||||
return dish.get(Dish.ARRAY_BUFFER);
|
||||
// Return an empty buffer if the recipe did not run to completion
|
||||
if (recipe.lastRunOp === recipe.opList[recipe.opList.length - 1]) {
|
||||
return dish.get(Dish.ARRAY_BUFFER);
|
||||
} else {
|
||||
return new ArrayBuffer();
|
||||
}
|
||||
} catch (err) {
|
||||
// If there are errors, return an empty buffer
|
||||
return new ArrayBuffer();
|
||||
|
@ -440,7 +450,7 @@ class Magic {
|
|||
const opPatterns = [];
|
||||
|
||||
for (const op in OperationConfig) {
|
||||
if (!OperationConfig[op].hasOwnProperty("patterns")) continue;
|
||||
if (!("patterns" in OperationConfig[op])) continue;
|
||||
|
||||
OperationConfig[op].patterns.forEach(pattern => {
|
||||
opPatterns.push({
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import OperationError from "../errors/OperationError";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import kbpgp from "kbpgp";
|
||||
import * as es6promisify from "es6-promisify";
|
||||
const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
|
||||
|
@ -45,7 +46,7 @@ export const ASP = kbpgp.ASP({
|
|||
msg = `Stage: ${info.what}`;
|
||||
}
|
||||
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
if (isWorkerEnvironment())
|
||||
self.sendStatusMessage(msg);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import Utils from "../Utils";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Protobuf lib. Contains functions to decode protobuf serialised
|
||||
|
@ -16,14 +16,14 @@ class Protobuf {
|
|||
/**
|
||||
* Protobuf constructor
|
||||
*
|
||||
* @param {byteArray} data
|
||||
* @param {byteArray|Uint8Array} data
|
||||
*/
|
||||
constructor(data) {
|
||||
// Check we have a byteArray
|
||||
if (data instanceof Array) {
|
||||
// Check we have a byteArray or Uint8Array
|
||||
if (data instanceof Array || data instanceof Uint8Array) {
|
||||
this.data = data;
|
||||
} else {
|
||||
throw new Error("Protobuf input must be a byteArray");
|
||||
throw new Error("Protobuf input must be a byteArray or Uint8Array");
|
||||
}
|
||||
|
||||
// Set up masks
|
||||
|
@ -124,7 +124,7 @@ class Protobuf {
|
|||
// Get the field key/values
|
||||
const key = field.key;
|
||||
const value = field.value;
|
||||
object[key] = object.hasOwnProperty(key) ?
|
||||
object[key] = Object.prototype.hasOwnProperty.call(object, key) ?
|
||||
object[key] instanceof Array ?
|
||||
object[key].concat([value]) :
|
||||
[object[key], value] :
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import { toHex, fromHex } from "./Hex";
|
||||
import { toHex, fromHex } from "./Hex.mjs";
|
||||
|
||||
/**
|
||||
* Formats Distinguished Name (DN) strings.
|
||||
|
|
93
src/core/lib/QRCode.mjs
Normal file
93
src/core/lib/QRCode.mjs
Normal file
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* QR code resources
|
||||
*
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import jsQR from "jsqr";
|
||||
import qr from "qr-image";
|
||||
import jimp from "jimp";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Parses a QR code image from an image
|
||||
*
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {boolean} normalise
|
||||
* @returns {string}
|
||||
*/
|
||||
export async function parseQrCode(input, normalise) {
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error opening image. (${err})`);
|
||||
}
|
||||
|
||||
try {
|
||||
if (normalise) {
|
||||
image.rgba(false);
|
||||
image.background(0xFFFFFFFF);
|
||||
image.normalize();
|
||||
image.greyscale();
|
||||
image = await image.getBufferAsync(jimp.MIME_JPEG);
|
||||
image = await jimp.read(image);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error normalising iamge. (${err})`);
|
||||
}
|
||||
|
||||
const qrData = jsQR(image.bitmap.data, image.getWidth(), image.getHeight());
|
||||
if (qrData) {
|
||||
return qrData.data;
|
||||
} else {
|
||||
throw new OperationError("Could not read a QR code from the image.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a QR code from the input string
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {string} format
|
||||
* @param {number} moduleSize
|
||||
* @param {number} margin
|
||||
* @param {string} errorCorrection
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
export function generateQrCode(input, format, moduleSize, margin, errorCorrection) {
|
||||
const formats = ["SVG", "EPS", "PDF", "PNG"];
|
||||
if (!formats.includes(format.toUpperCase())) {
|
||||
throw new OperationError("Unsupported QR code format.");
|
||||
}
|
||||
|
||||
let qrImage;
|
||||
try {
|
||||
qrImage = qr.imageSync(input, {
|
||||
type: format,
|
||||
size: moduleSize,
|
||||
margin: margin,
|
||||
"ec_level": errorCorrection.charAt(0).toUpperCase()
|
||||
});
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error generating QR code. (${err})`);
|
||||
}
|
||||
|
||||
if (!qrImage) {
|
||||
throw new OperationError("Error generating QR code.");
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case "SVG":
|
||||
case "EPS":
|
||||
case "PDF":
|
||||
return Utils.strToArrayBuffer(qrImage);
|
||||
case "PNG":
|
||||
return qrImage.buffer;
|
||||
default:
|
||||
throw new OperationError("Unsupported QR code format.");
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ export default class TLVParser {
|
|||
/**
|
||||
* TLVParser constructor
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @param {byteArray|Uint8Array} input
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(input, options) {
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import OperationError from "../errors/OperationError";
|
||||
import * as Enigma from "../lib/Enigma";
|
||||
import Utils from "../Utils";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import * as Enigma from "../lib/Enigma.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* A set of example Typex rotors. No Typex rotor wirings are publicly available, so these are
|
||||
|
@ -98,14 +98,14 @@ export class TypexMachine extends Enigma.EnigmaBase {
|
|||
if (x === " ") {
|
||||
inputMod += "X";
|
||||
} else if (mode) {
|
||||
if (KEYBOARD_REV.hasOwnProperty(x)) {
|
||||
if (Object.prototype.hasOwnProperty.call(KEYBOARD_REV, x)) {
|
||||
inputMod += KEYBOARD_REV[x];
|
||||
} else {
|
||||
mode = false;
|
||||
inputMod += "V" + x;
|
||||
}
|
||||
} else {
|
||||
if (KEYBOARD_REV.hasOwnProperty(x)) {
|
||||
if (Object.prototype.hasOwnProperty.call(KEYBOARD_REV, x)) {
|
||||
mode = true;
|
||||
inputMod += "Z" + KEYBOARD_REV[x];
|
||||
} else {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min";
|
||||
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min.js";
|
||||
|
||||
const Zlib = zlibAndGzip.Zlib;
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import {DELIM_OPTIONS} from "../lib/Delim";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import {DELIM_OPTIONS} from "../lib/Delim.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* A1Z26 Cipher Decode operation
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import {DELIM_OPTIONS} from "../lib/Delim";
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import {DELIM_OPTIONS} from "../lib/Delim.mjs";
|
||||
|
||||
/**
|
||||
* A1Z26 Cipher Encode operation
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import { bitOp, add, BITWISE_OP_DELIMS } from "../lib/BitwiseOp";
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import { bitOp, add, BITWISE_OP_DELIMS } from "../lib/BitwiseOp.mjs";
|
||||
|
||||
/**
|
||||
* ADD operation
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* AES Decrypt operation
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* AES Encrypt operation
|
||||
|
@ -65,8 +65,8 @@ class AESEncrypt extends Operation {
|
|||
* @throws {OperationError} if invalid key length
|
||||
*/
|
||||
run(input, args) {
|
||||
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
||||
const key = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteString(args[1].string, args[1].option),
|
||||
mode = args[2],
|
||||
inputType = args[3],
|
||||
outputType = args[4];
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import { bitOp, and, BITWISE_OP_DELIMS } from "../lib/BitwiseOp";
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import { bitOp, and, BITWISE_OP_DELIMS } from "../lib/BitwiseOp.mjs";
|
||||
|
||||
/**
|
||||
* AND operation
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* Add line numbers operation
|
||||
|
|
267
src/core/operations/AddTextToImage.mjs
Normal file
267
src/core/operations/AddTextToImage.mjs
Normal file
|
@ -0,0 +1,267 @@
|
|||
/**
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
|
||||
/**
|
||||
* Add Text To Image operation
|
||||
*/
|
||||
class AddTextToImage extends Operation {
|
||||
|
||||
/**
|
||||
* AddTextToImage constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Add Text To Image";
|
||||
this.module = "Image";
|
||||
this.description = "Adds text onto an image.<br><br>Text can be horizontally or vertically aligned, or the position can be manually specified.<br>Variants of the Roboto font face are available in any size or colour.";
|
||||
this.infoURL = "";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
name: "Text",
|
||||
type: "string",
|
||||
value: ""
|
||||
},
|
||||
{
|
||||
name: "Horizontal align",
|
||||
type: "option",
|
||||
value: ["None", "Left", "Center", "Right"]
|
||||
},
|
||||
{
|
||||
name: "Vertical align",
|
||||
type: "option",
|
||||
value: ["None", "Top", "Middle", "Bottom"]
|
||||
},
|
||||
{
|
||||
name: "X position",
|
||||
type: "number",
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: "Y position",
|
||||
type: "number",
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: "Size",
|
||||
type: "number",
|
||||
value: 32,
|
||||
min: 8
|
||||
},
|
||||
{
|
||||
name: "Font face",
|
||||
type: "option",
|
||||
value: [
|
||||
"Roboto",
|
||||
"Roboto Black",
|
||||
"Roboto Mono",
|
||||
"Roboto Slab"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Red",
|
||||
type: "number",
|
||||
value: 255,
|
||||
min: 0,
|
||||
max: 255
|
||||
},
|
||||
{
|
||||
name: "Green",
|
||||
type: "number",
|
||||
value: 255,
|
||||
min: 0,
|
||||
max: 255
|
||||
},
|
||||
{
|
||||
name: "Blue",
|
||||
type: "number",
|
||||
value: 255,
|
||||
min: 0,
|
||||
max: 255
|
||||
},
|
||||
{
|
||||
name: "Alpha",
|
||||
type: "number",
|
||||
value: 255,
|
||||
min: 0,
|
||||
max: 255
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const text = args[0],
|
||||
hAlign = args[1],
|
||||
vAlign = args[2],
|
||||
size = args[5],
|
||||
fontFace = args[6],
|
||||
red = args[7],
|
||||
green = args[8],
|
||||
blue = args[9],
|
||||
alpha = args[10];
|
||||
|
||||
let xPos = args[3],
|
||||
yPos = args[4];
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
try {
|
||||
if (isWorkerEnvironment())
|
||||
self.sendStatusMessage("Adding text to image...");
|
||||
|
||||
const fontsMap = {};
|
||||
const fonts = [
|
||||
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.fnt"),
|
||||
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoBlack72White.fnt"),
|
||||
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoMono72White.fnt"),
|
||||
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoSlab72White.fnt")
|
||||
];
|
||||
|
||||
await Promise.all(fonts)
|
||||
.then(fonts => {
|
||||
fontsMap.Roboto = fonts[0];
|
||||
fontsMap["Roboto Black"] = fonts[1];
|
||||
fontsMap["Roboto Mono"] = fonts[2];
|
||||
fontsMap["Roboto Slab"] = fonts[3];
|
||||
});
|
||||
|
||||
|
||||
// Make Webpack load the png font images
|
||||
await Promise.all([
|
||||
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.png"),
|
||||
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoSlab72White.png"),
|
||||
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoMono72White.png"),
|
||||
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoBlack72White.png")
|
||||
]);
|
||||
|
||||
const font = fontsMap[fontFace];
|
||||
|
||||
// LoadFont needs an absolute url, so append the font name to self.docURL
|
||||
const jimpFont = await jimp.loadFont(self.docURL + "/" + font.default);
|
||||
|
||||
jimpFont.pages.forEach(function(page) {
|
||||
if (page.bitmap) {
|
||||
// Adjust the RGB values of the image pages to change the font colour.
|
||||
const pageWidth = page.bitmap.width;
|
||||
const pageHeight = page.bitmap.height;
|
||||
for (let ix = 0; ix < pageWidth; ix++) {
|
||||
for (let iy = 0; iy < pageHeight; iy++) {
|
||||
const idx = (iy * pageWidth + ix) << 2;
|
||||
|
||||
const newRed = page.bitmap.data[idx] - (255 - red);
|
||||
const newGreen = page.bitmap.data[idx + 1] - (255 - green);
|
||||
const newBlue = page.bitmap.data[idx + 2] - (255 - blue);
|
||||
const newAlpha = page.bitmap.data[idx + 3] - (255 - alpha);
|
||||
|
||||
// Make sure the bitmap values don't go below 0 as that makes jimp very unhappy
|
||||
page.bitmap.data[idx] = (newRed > 0) ? newRed : 0;
|
||||
page.bitmap.data[idx + 1] = (newGreen > 0) ? newGreen : 0;
|
||||
page.bitmap.data[idx + 2] = (newBlue > 0) ? newBlue : 0;
|
||||
page.bitmap.data[idx + 3] = (newAlpha > 0) ? newAlpha : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Create a temporary image to hold the rendered text
|
||||
const textImage = new jimp(jimp.measureText(jimpFont, text), jimp.measureTextHeight(jimpFont, text));
|
||||
textImage.print(jimpFont, 0, 0, text);
|
||||
|
||||
// Scale the rendered text image to the correct size
|
||||
const scaleFactor = size / 72;
|
||||
if (size !== 1) {
|
||||
// Use bicubic for decreasing size
|
||||
if (size > 1) {
|
||||
textImage.scale(scaleFactor, jimp.RESIZE_BICUBIC);
|
||||
} else {
|
||||
textImage.scale(scaleFactor, jimp.RESIZE_BILINEAR);
|
||||
}
|
||||
}
|
||||
|
||||
// If using the alignment options, calculate the pixel values AFTER the image has been scaled
|
||||
switch (hAlign) {
|
||||
case "Left":
|
||||
xPos = 0;
|
||||
break;
|
||||
case "Center":
|
||||
xPos = (image.getWidth() / 2) - (textImage.getWidth() / 2);
|
||||
break;
|
||||
case "Right":
|
||||
xPos = image.getWidth() - textImage.getWidth();
|
||||
break;
|
||||
}
|
||||
|
||||
switch (vAlign) {
|
||||
case "Top":
|
||||
yPos = 0;
|
||||
break;
|
||||
case "Middle":
|
||||
yPos = (image.getHeight() / 2) - (textImage.getHeight() / 2);
|
||||
break;
|
||||
case "Bottom":
|
||||
yPos = image.getHeight() - textImage.getHeight();
|
||||
break;
|
||||
}
|
||||
|
||||
// Blit the rendered text image onto the original source image
|
||||
image.blit(textImage, xPos, yPos);
|
||||
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error adding text to image. (${err})`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the blurred image using HTML for web apps
|
||||
*
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default AddTextToImage;
|
|
@ -4,8 +4,8 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Adler-32 Checksum operation
|
||||
|
@ -22,13 +22,13 @@ class Adler32Checksum extends Operation {
|
|||
this.module = "Crypto";
|
||||
this.description = "Adler-32 is a checksum algorithm which was invented by Mark Adler in 1995, and is a modification of the Fletcher checksum. Compared to a cyclic redundancy check of the same length, it trades reliability for speed (preferring the latter).<br><br>Adler-32 is more reliable than Fletcher-16, and slightly less reliable than Fletcher-32.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Adler-32";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
|
@ -36,6 +36,7 @@ class Adler32Checksum extends Operation {
|
|||
const MOD_ADLER = 65521;
|
||||
let a = 1,
|
||||
b = 0;
|
||||
input = new Uint8Array(input);
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
a += input[i];
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Affine Cipher Decode operation
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import { affineEncode } from "../lib/Ciphers";
|
||||
import Operation from "../Operation.mjs";
|
||||
import { affineEncode } from "../lib/Ciphers.mjs";
|
||||
|
||||
/**
|
||||
* Affine Cipher Encode operation
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Analyse hash operation
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import { affineEncode } from "../lib/Ciphers";
|
||||
import Operation from "../Operation.mjs";
|
||||
import { affineEncode } from "../lib/Ciphers.mjs";
|
||||
|
||||
/**
|
||||
* Atbash Cipher operation
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
import blakejs from "blakejs";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Utils from "../Utils";
|
||||
import { toBase64 } from "../lib/Base64";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
|
||||
/**
|
||||
* BLAKE2b operation
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
import blakejs from "blakejs";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Utils from "../Utils";
|
||||
import { toBase64 } from "../lib/Base64";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
|
||||
/**
|
||||
* BLAKE2s Operation
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
import bson from "bson";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* BSON deserialise operation
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
import bson from "bson";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* BSON serialise operation
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Bcrypt operation
|
||||
|
@ -44,7 +45,7 @@ class Bcrypt extends Operation {
|
|||
|
||||
return await bcrypt.hash(input, salt, null, p => {
|
||||
// Progress callback
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
if (isWorkerEnvironment())
|
||||
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
|
||||
});
|
||||
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* Bcrypt compare operation
|
||||
|
@ -43,7 +45,7 @@ class BcryptCompare extends Operation {
|
|||
|
||||
const match = await bcrypt.compare(input, hash, null, p => {
|
||||
// Progress callback
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
if (isWorkerEnvironment())
|
||||
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
|
||||
});
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import bcrypt from "bcryptjs";
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import { genPolybiusSquare } from "../lib/Ciphers";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Operation from "../Operation.mjs";
|
||||
import { genPolybiusSquare } from "../lib/Ciphers.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Bifid Cipher Decode operation
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import { genPolybiusSquare } from "../lib/Ciphers";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { genPolybiusSquare } from "../lib/Ciphers.mjs";
|
||||
|
||||
/**
|
||||
* Bifid Cipher Encode operation
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* Bit shift left operation
|
||||
|
@ -21,8 +21,8 @@ class BitShiftLeft extends Operation {
|
|||
this.module = "Default";
|
||||
this.description = "Shifts the bits in each byte towards the left by the specified amount.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bit_shifts";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Amount",
|
||||
|
@ -33,16 +33,17 @@ class BitShiftLeft extends Operation {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
run(input, args) {
|
||||
const amount = args[0];
|
||||
input = new Uint8Array(input);
|
||||
|
||||
return input.map(b => {
|
||||
return (b << amount) & 0xff;
|
||||
});
|
||||
}).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* Bit shift right operation
|
||||
|
@ -21,8 +21,8 @@ class BitShiftRight extends Operation {
|
|||
this.module = "Default";
|
||||
this.description = "Shifts the bits in each byte towards the right by the specified amount.<br><br><i>Logical shifts</i> replace the leftmost bits with zeros.<br><i>Arithmetic shifts</i> preserve the most significant bit (MSB) of the original byte keeping the sign the same (positive or negative).";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bit_shifts";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Amount",
|
||||
|
@ -38,18 +38,19 @@ class BitShiftRight extends Operation {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
run(input, args) {
|
||||
const amount = args[0],
|
||||
type = args[1],
|
||||
mask = type === "Logical shift" ? 0 : 0x80;
|
||||
input = new Uint8Array(input);
|
||||
|
||||
return input.map(b => {
|
||||
return (b >>> amount) ^ (b & mask);
|
||||
});
|
||||
}).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import { Blowfish } from "../vendor/Blowfish";
|
||||
import { toBase64 } from "../lib/Base64";
|
||||
import { toHexFast } from "../lib/Hex";
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { Blowfish } from "../vendor/Blowfish.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { toHexFast } from "../lib/Hex.mjs";
|
||||
|
||||
/**
|
||||
* Lookup table for Blowfish output types.
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import { Blowfish } from "../vendor/Blowfish";
|
||||
import { toBase64 } from "../lib/Base64";
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { Blowfish } from "../vendor/Blowfish.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
|
||||
/**
|
||||
* Lookup table for Blowfish output types.
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import { isImage } from "../lib/FileType";
|
||||
import { toBase64 } from "../lib/Base64";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import jimp from "jimp";
|
||||
import { gaussianBlur } from "../lib/ImageManipulation.mjs";
|
||||
|
||||
/**
|
||||
* Blur Image operation
|
||||
|
@ -25,8 +27,8 @@ class BlurImage extends Operation {
|
|||
this.module = "Image";
|
||||
this.description = "Applies a blur effect to the image.<br><br>Gaussian blur is much slower than fast blur, but produces better results.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Gaussian_blur";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
|
@ -44,37 +46,44 @@ class BlurImage extends Operation {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [blurAmount, blurType] = args;
|
||||
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
try {
|
||||
switch (blurType){
|
||||
case "Fast":
|
||||
if (isWorkerEnvironment())
|
||||
self.sendStatusMessage("Fast blurring image...");
|
||||
image.blur(blurAmount);
|
||||
break;
|
||||
case "Gaussian":
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
self.sendStatusMessage("Gaussian blurring image. This may take a while...");
|
||||
image.gaussian(blurAmount);
|
||||
if (isWorkerEnvironment())
|
||||
self.sendStatusMessage("Gaussian blurring image...");
|
||||
image = gaussianBlur(image, blurAmount);
|
||||
break;
|
||||
}
|
||||
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error blurring image. (${err})`);
|
||||
}
|
||||
|
@ -83,18 +92,19 @@ class BlurImage extends Operation {
|
|||
/**
|
||||
* Displays the blurred image using HTML for web apps
|
||||
*
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import {BombeMachine} from "../lib/Bombe";
|
||||
import {ROTORS, ROTORS_FOURTH, REFLECTORS, Reflector} from "../lib/Enigma";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import { BombeMachine } from "../lib/Bombe.mjs";
|
||||
import { ROTORS, ROTORS_FOURTH, REFLECTORS, Reflector } from "../lib/Enigma.mjs";
|
||||
|
||||
/**
|
||||
* Bombe operation
|
||||
|
@ -139,7 +140,7 @@ class Bombe extends Operation {
|
|||
const ciphertext = input.slice(offset);
|
||||
const reflector = new Reflector(reflectorstr);
|
||||
let update;
|
||||
if (ENVIRONMENT_IS_WORKER()) {
|
||||
if (isWorkerEnvironment()) {
|
||||
update = this.updateStatus;
|
||||
} else {
|
||||
update = undefined;
|
||||
|
|
73
src/core/operations/Bzip2Compress.mjs
Normal file
73
src/core/operations/Bzip2Compress.mjs
Normal file
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* @author Matt C [me@mitt.dev]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Bzip2 from "libbzip2-wasm";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Bzip2 Compress operation
|
||||
*/
|
||||
class Bzip2Compress extends Operation {
|
||||
|
||||
/**
|
||||
* Bzip2Compress constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Bzip2 Compress";
|
||||
this.module = "Compression";
|
||||
this.description = "Bzip2 is a compression library developed by Julian Seward (of GHC fame) that uses the Burrows-Wheeler algorithm. It only supports compressing single files and its compression is slow, however is more effective than Deflate (.gz & .zip).";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bzip2";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
name: "Block size (100s of kb)",
|
||||
type: "number",
|
||||
value: 9,
|
||||
min: 1,
|
||||
max: 9
|
||||
},
|
||||
{
|
||||
name: "Work factor",
|
||||
type: "number",
|
||||
value: 30
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {File}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [blockSize, workFactor] = args;
|
||||
if (input.byteLength <= 0) {
|
||||
throw new OperationError("Please provide an input.");
|
||||
}
|
||||
if (isWorkerEnvironment()) self.sendStatusMessage("Loading Bzip2...");
|
||||
return new Promise((resolve, reject) => {
|
||||
Bzip2().then(bzip2 => {
|
||||
if (isWorkerEnvironment()) self.sendStatusMessage("Compressing data...");
|
||||
const inpArray = new Uint8Array(input);
|
||||
const bzip2cc = bzip2.compressBZ2(inpArray, blockSize, workFactor);
|
||||
if (bzip2cc.error !== 0) {
|
||||
reject(new OperationError(bzip2cc.error_msg));
|
||||
} else {
|
||||
const output = bzip2cc.output;
|
||||
resolve(output.buffer.slice(output.byteOffset, output.byteLength + output.byteOffset));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Bzip2Compress;
|
|
@ -1,12 +1,13 @@
|
|||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @author Matt C [me@mitt.dev]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import bzip2 from "../vendor/bzip2";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Bzip2 from "libbzip2-wasm";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Bzip2 Decompress operation
|
||||
|
@ -23,9 +24,15 @@ class Bzip2Decompress extends Operation {
|
|||
this.module = "Compression";
|
||||
this.description = "Decompresses data using the Bzip2 algorithm.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bzip2";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
name: "Use low-memory, slower decompression algorithm",
|
||||
type: "boolean",
|
||||
value: false
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
{
|
||||
"match": "^\\x42\\x5a\\x68",
|
||||
|
@ -41,14 +48,24 @@ class Bzip2Decompress extends Operation {
|
|||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const compressed = new Uint8Array(input);
|
||||
|
||||
try {
|
||||
const bzip2Reader = bzip2.array(compressed);
|
||||
return bzip2.simple(bzip2Reader);
|
||||
} catch (err) {
|
||||
throw new OperationError(err);
|
||||
const [small] = args;
|
||||
if (input.byteLength <= 0) {
|
||||
throw new OperationError("Please provide an input.");
|
||||
}
|
||||
if (isWorkerEnvironment()) self.sendStatusMessage("Loading Bzip2...");
|
||||
return new Promise((resolve, reject) => {
|
||||
Bzip2().then(bzip2 => {
|
||||
if (isWorkerEnvironment()) self.sendStatusMessage("Decompressing data...");
|
||||
const inpArray = new Uint8Array(input);
|
||||
const bzip2cc = bzip2.decompressBZ2(inpArray, small ? 1 : 0);
|
||||
if (bzip2cc.error !== 0) {
|
||||
reject(new OperationError(bzip2cc.error_msg));
|
||||
} else {
|
||||
const output = bzip2cc.output;
|
||||
resolve(output.buffer.slice(output.byteOffset, output.byteLength + output.byteOffset));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
import JSCRC from "js-crc";
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
import JSCRC from "js-crc";
|
||||
|
||||
/**
|
||||
|
|
157
src/core/operations/CRC8Checksum.mjs
Normal file
157
src/core/operations/CRC8Checksum.mjs
Normal file
|
@ -0,0 +1,157 @@
|
|||
/**
|
||||
* @author mshwed [m@ttshwed.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
import { toHexFast } from "../lib/Hex.mjs";
|
||||
|
||||
/**
|
||||
* CRC-8 Checksum operation
|
||||
*/
|
||||
class CRC8Checksum extends Operation {
|
||||
|
||||
/**
|
||||
* CRC8Checksum constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "CRC-8 Checksum";
|
||||
this.module = "Crypto";
|
||||
this.description = "A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data.<br><br>The CRC was invented by W. Wesley Peterson in 1961.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Cyclic_redundancy_check";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Algorithm",
|
||||
"type": "option",
|
||||
"value": [
|
||||
"CRC-8",
|
||||
"CRC-8/CDMA2000",
|
||||
"CRC-8/DARC",
|
||||
"CRC-8/DVB-S2",
|
||||
"CRC-8/EBU",
|
||||
"CRC-8/I-CODE",
|
||||
"CRC-8/ITU",
|
||||
"CRC-8/MAXIM",
|
||||
"CRC-8/ROHC",
|
||||
"CRC-8/WCDMA"
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the pre-computed lookup table for byte division
|
||||
*
|
||||
* @param polynomial
|
||||
*/
|
||||
calculateCRC8LookupTable(polynomial) {
|
||||
const crc8Table = new Uint8Array(256);
|
||||
|
||||
let currentByte;
|
||||
for (let i = 0; i < 256; i++) {
|
||||
currentByte = i;
|
||||
for (let bit = 0; bit < 8; bit++) {
|
||||
if ((currentByte & 0x80) !== 0) {
|
||||
currentByte <<= 1;
|
||||
currentByte ^= polynomial;
|
||||
} else {
|
||||
currentByte <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
crc8Table[i] = currentByte;
|
||||
}
|
||||
|
||||
return crc8Table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the CRC-8 Checksum from an input
|
||||
*
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {number} polynomial
|
||||
* @param {number} initializationValue
|
||||
* @param {boolean} inputReflection
|
||||
* @param {boolean} outputReflection
|
||||
* @param {number} xorOut
|
||||
*/
|
||||
calculateCRC8(input, polynomial, initializationValue, inputReflection, outputReflection, xorOut) {
|
||||
const crcSize = 8;
|
||||
const crcTable = this.calculateCRC8LookupTable(polynomial);
|
||||
|
||||
let crc = initializationValue !== 0 ? initializationValue : 0;
|
||||
let currentByte, position;
|
||||
|
||||
input = new Uint8Array(input);
|
||||
for (const inputByte of input) {
|
||||
currentByte = inputReflection ? this.reverseBits(inputByte, crcSize) : inputByte;
|
||||
|
||||
position = (currentByte ^ crc) & 255;
|
||||
crc = crcTable[position];
|
||||
}
|
||||
|
||||
crc = outputReflection ? this.reverseBits(crc, crcSize) : crc;
|
||||
|
||||
if (xorOut !== 0) crc = crc ^ xorOut;
|
||||
|
||||
return toHexFast(new Uint8Array([crc]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the bits for a given input byte.
|
||||
*
|
||||
* @param {number} input
|
||||
*/
|
||||
reverseBits(input, hashSize) {
|
||||
let reversedByte = 0;
|
||||
for (let i = hashSize - 1; i >= 0; i--) {
|
||||
reversedByte |= ((input & 1) << i);
|
||||
input >>= 1;
|
||||
}
|
||||
|
||||
return reversedByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const algorithm = args[0];
|
||||
|
||||
switch (algorithm) {
|
||||
case "CRC-8":
|
||||
return this.calculateCRC8(input, 0x7, 0x0, false, false, 0x0);
|
||||
case "CRC-8/CDMA2000":
|
||||
return this.calculateCRC8(input, 0x9B, 0xFF, false, false, 0x0);
|
||||
case "CRC-8/DARC":
|
||||
return this.calculateCRC8(input, 0x39, 0x0, true, true, 0x0);
|
||||
case "CRC-8/DVB-S2":
|
||||
return this.calculateCRC8(input, 0xD5, 0x0, false, false, 0x0);
|
||||
case "CRC-8/EBU":
|
||||
return this.calculateCRC8(input, 0x1D, 0xFF, true, true, 0x0);
|
||||
case "CRC-8/I-CODE":
|
||||
return this.calculateCRC8(input, 0x1D, 0xFD, false, false, 0x0);
|
||||
case "CRC-8/ITU":
|
||||
return this.calculateCRC8(input, 0x7, 0x0, false, false, 0x55);
|
||||
case "CRC-8/MAXIM":
|
||||
return this.calculateCRC8(input, 0x31, 0x0, true, true, 0x0);
|
||||
case "CRC-8/ROHC":
|
||||
return this.calculateCRC8(input, 0x7, 0xFF, true, true, 0x0);
|
||||
case "CRC-8/WCDMA":
|
||||
return this.calculateCRC8(input, 0x9B, 0x0, true, true, 0x0);
|
||||
default:
|
||||
throw new OperationError("Unknown checksum algorithm");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default CRC8Checksum;
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import vkbeautify from "vkbeautify";
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* CSS Beautify operation
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import vkbeautify from "vkbeautify";
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* CSS Minify operation
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import xmldom from "xmldom";
|
||||
import nwmatcher from "nwmatcher";
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Utils from "../Utils";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* CSV to JSON operation
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
import ctphjs from "ctph.js";
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Set cartesian product operation
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Utils from "../Utils";
|
||||
import {fromHex} from "../lib/Hex";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import {fromHex} from "../lib/Hex.mjs";
|
||||
|
||||
/**
|
||||
* Change IP format operation
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* Chi Square operation
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import cptable from "../vendor/js-codepage/cptable.js";
|
||||
|
||||
/**
|
||||
|
@ -23,17 +23,18 @@ class CitrixCTX1Decode extends Operation {
|
|||
this.module = "Encodings";
|
||||
this.description = "Decodes strings in a Citrix CTX1 password format to plaintext.";
|
||||
this.infoURL = "https://www.reddit.com/r/AskNetsec/comments/1s3r6y/citrix_ctx1_hash_decoding/";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
if (input.length % 4 !== 0) {
|
||||
throw new OperationError("Incorrect hash length");
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
import cptable from "../vendor/js-codepage/cptable.js";
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* Comment operation
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import {HASH_DELIM_OPTIONS} from "../lib/Delim";
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import {HASH_DELIM_OPTIONS} from "../lib/Delim.mjs";
|
||||
import ctphjs from "ctph.js";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Compare CTPH hashes operation
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import {HASH_DELIM_OPTIONS} from "../lib/Delim";
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import {HASH_DELIM_OPTIONS} from "../lib/Delim.mjs";
|
||||
import ssdeepjs from "ssdeep.js";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
* Compare SSDEEP hashes operation
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Dish from "../Dish";
|
||||
import { getLabelIndex } from "../lib/FlowControl";
|
||||
import Operation from "../Operation.mjs";
|
||||
import Dish from "../Dish.mjs";
|
||||
import { getLabelIndex } from "../lib/FlowControl.mjs";
|
||||
|
||||
/**
|
||||
* Conditional Jump operation
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import { isImage } from "../lib/FileType";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
|
||||
/**
|
||||
|
@ -25,8 +26,8 @@ class ContainImage extends Operation {
|
|||
this.module = "Image";
|
||||
this.description = "Scales an image to the specified width and height, maintaining the aspect ratio. The image may be letterboxed.";
|
||||
this.infoURL = "";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
|
@ -72,17 +73,22 @@ class ContainImage extends Operation {
|
|||
"Bezier"
|
||||
],
|
||||
defaultIndex: 1
|
||||
},
|
||||
{
|
||||
name: "Opaque background",
|
||||
type: "boolean",
|
||||
value: true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [width, height, hAlign, vAlign, alg] = args;
|
||||
const [width, height, hAlign, vAlign, alg, opaqueBg] = args;
|
||||
|
||||
const resizeMap = {
|
||||
"Nearest Neighbour": jimp.RESIZE_NEAREST_NEIGHBOR,
|
||||
|
@ -101,22 +107,34 @@ class ContainImage extends Operation {
|
|||
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
|
||||
};
|
||||
|
||||
if (!isImage(input)) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(Buffer.from(input));
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error loading image. (${err})`);
|
||||
}
|
||||
try {
|
||||
if (ENVIRONMENT_IS_WORKER())
|
||||
if (isWorkerEnvironment())
|
||||
self.sendStatusMessage("Containing image...");
|
||||
image.contain(width, height, alignMap[hAlign] | alignMap[vAlign], resizeMap[alg]);
|
||||
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
return [...imageBuffer];
|
||||
|
||||
if (opaqueBg) {
|
||||
const newImage = await jimp.read(width, height, 0x000000FF);
|
||||
newImage.blit(image, 0, 0);
|
||||
image = newImage;
|
||||
}
|
||||
|
||||
let imageBuffer;
|
||||
if (image.getMIME() === "image/gif") {
|
||||
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
|
||||
} else {
|
||||
imageBuffer = await image.getBufferAsync(jimp.AUTO);
|
||||
}
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error containing image. (${err})`);
|
||||
}
|
||||
|
@ -124,18 +142,19 @@ class ContainImage extends Operation {
|
|||
|
||||
/**
|
||||
* Displays the contained image using HTML for web apps
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(data);
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* Convert area operation
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import {FORMATS, convertCoordinates} from "../lib/ConvertCoordinates";
|
||||
import Operation from "../Operation.mjs";
|
||||
import {FORMATS, convertCoordinates} from "../lib/ConvertCoordinates.mjs";
|
||||
|
||||
/**
|
||||
* Convert co-ordinate format operation
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* Convert data units operation
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* Convert distance operation
|
||||
|
|
143
src/core/operations/ConvertImageFormat.mjs
Normal file
143
src/core/operations/ConvertImageFormat.mjs
Normal file
|
@ -0,0 +1,143 @@
|
|||
/**
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import jimp from "jimp";
|
||||
|
||||
/**
|
||||
* Convert Image Format operation
|
||||
*/
|
||||
class ConvertImageFormat extends Operation {
|
||||
|
||||
/**
|
||||
* ConvertImageFormat constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Convert Image Format";
|
||||
this.module = "Image";
|
||||
this.description = "Converts an image between different formats. Supported formats:<br><ul><li>Joint Photographic Experts Group (JPEG)</li><li>Portable Network Graphics (PNG)</li><li>Bitmap (BMP)</li><li>Tagged Image File Format (TIFF)</li></ul><br>Note: GIF files are supported for input, but cannot be outputted.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Image_file_formats";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
name: "Output Format",
|
||||
type: "option",
|
||||
value: [
|
||||
"JPEG",
|
||||
"PNG",
|
||||
"BMP",
|
||||
"TIFF"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "JPEG Quality",
|
||||
type: "number",
|
||||
value: 80,
|
||||
min: 1,
|
||||
max: 100
|
||||
},
|
||||
{
|
||||
name: "PNG Filter Type",
|
||||
type: "option",
|
||||
value: [
|
||||
"Auto",
|
||||
"None",
|
||||
"Sub",
|
||||
"Up",
|
||||
"Average",
|
||||
"Paeth"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "PNG Deflate Level",
|
||||
type: "number",
|
||||
value: 9,
|
||||
min: 0,
|
||||
max: 9
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const [format, jpegQuality, pngFilterType, pngDeflateLevel] = args;
|
||||
const formatMap = {
|
||||
"JPEG": jimp.MIME_JPEG,
|
||||
"PNG": jimp.MIME_PNG,
|
||||
"BMP": jimp.MIME_BMP,
|
||||
"TIFF": jimp.MIME_TIFF
|
||||
};
|
||||
|
||||
const pngFilterMap = {
|
||||
"Auto": jimp.PNG_FILTER_AUTO,
|
||||
"None": jimp.PNG_FILTER_NONE,
|
||||
"Sub": jimp.PNG_FILTER_SUB,
|
||||
"Up": jimp.PNG_FILTER_UP,
|
||||
"Average": jimp.PNG_FILTER_AVERAGE,
|
||||
"Paeth": jimp.PNG_FILTER_PATH // Incorrect spelling in Jimp library
|
||||
};
|
||||
|
||||
const mime = formatMap[format];
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
throw new OperationError("Invalid file format.");
|
||||
}
|
||||
let image;
|
||||
try {
|
||||
image = await jimp.read(input);
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error opening image file. (${err})`);
|
||||
}
|
||||
try {
|
||||
switch (format) {
|
||||
case "JPEG":
|
||||
image.quality(jpegQuality);
|
||||
break;
|
||||
case "PNG":
|
||||
image.filterType(pngFilterMap[pngFilterType]);
|
||||
image.deflateLevel(pngDeflateLevel);
|
||||
break;
|
||||
}
|
||||
|
||||
const imageBuffer = await image.getBufferAsync(mime);
|
||||
return imageBuffer.buffer;
|
||||
} catch (err) {
|
||||
throw new OperationError(`Error converting image format. (${err})`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the converted image using HTML for web apps
|
||||
*
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.byteLength) return "";
|
||||
const dataArray = new Uint8Array(data);
|
||||
|
||||
const type = isImage(dataArray);
|
||||
if (!type) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ConvertImageFormat;
|
|
@ -4,7 +4,7 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* Convert mass operation
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue