Merge branch 'master' of github.com:gchq/CyberChef into dynamic-import
commit
481f2a4717
|
@ -2,7 +2,10 @@
|
||||||
All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master).
|
All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master).
|
||||||
|
|
||||||
|
|
||||||
### [8.24.0] - 2019-01-18
|
### [8.24.0] - 2019-02-08
|
||||||
|
- 'DNS over HTTPS' operation added [@h345983745] | [#489]
|
||||||
|
|
||||||
|
### [8.23.1] - 2019-01-18
|
||||||
- 'Convert co-ordinate format' operation added [@j433866] | [#476]
|
- 'Convert co-ordinate format' operation added [@j433866] | [#476]
|
||||||
|
|
||||||
### [8.23.0] - 2019-01-18
|
### [8.23.0] - 2019-01-18
|
||||||
|
@ -104,6 +107,7 @@ All major and minor version changes will be documented in this file. Details of
|
||||||
|
|
||||||
|
|
||||||
[8.24.0]: https://github.com/gchq/CyberChef/releases/tag/v8.24.0
|
[8.24.0]: https://github.com/gchq/CyberChef/releases/tag/v8.24.0
|
||||||
|
[8.23.1]: https://github.com/gchq/CyberChef/releases/tag/v8.23.1
|
||||||
[8.23.0]: https://github.com/gchq/CyberChef/releases/tag/v8.23.0
|
[8.23.0]: https://github.com/gchq/CyberChef/releases/tag/v8.23.0
|
||||||
[8.22.0]: https://github.com/gchq/CyberChef/releases/tag/v8.22.0
|
[8.22.0]: https://github.com/gchq/CyberChef/releases/tag/v8.22.0
|
||||||
[8.21.0]: https://github.com/gchq/CyberChef/releases/tag/v8.21.0
|
[8.21.0]: https://github.com/gchq/CyberChef/releases/tag/v8.21.0
|
||||||
|
@ -137,6 +141,7 @@ All major and minor version changes will be documented in this file. Details of
|
||||||
[@d98762625]: https://github.com/d98762625
|
[@d98762625]: https://github.com/d98762625
|
||||||
[@j433866]: https://github.com/j433866
|
[@j433866]: https://github.com/j433866
|
||||||
[@GCHQ77703]: https://github.com/GCHQ77703
|
[@GCHQ77703]: https://github.com/GCHQ77703
|
||||||
|
[@h345983745]: https://github.com/h345983745
|
||||||
[@artemisbot]: https://github.com/artemisbot
|
[@artemisbot]: https://github.com/artemisbot
|
||||||
[@picapi]: https://github.com/picapi
|
[@picapi]: https://github.com/picapi
|
||||||
[@Dachande663]: https://github.com/Dachande663
|
[@Dachande663]: https://github.com/Dachande663
|
||||||
|
@ -186,3 +191,4 @@ All major and minor version changes will be documented in this file. Details of
|
||||||
[#467]: https://github.com/gchq/CyberChef/pull/467
|
[#467]: https://github.com/gchq/CyberChef/pull/467
|
||||||
[#468]: https://github.com/gchq/CyberChef/pull/468
|
[#468]: https://github.com/gchq/CyberChef/pull/468
|
||||||
[#476]: https://github.com/gchq/CyberChef/pull/476
|
[#476]: https://github.com/gchq/CyberChef/pull/476
|
||||||
|
[#489]: https://github.com/gchq/CyberChef/pull/489
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "cyberchef",
|
"name": "cyberchef",
|
||||||
"version": "8.23.0",
|
"version": "8.24.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -766,6 +766,22 @@
|
||||||
"semver": "^5.3.0"
|
"semver": "^5.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@babel/runtime-corejs2": {
|
||||||
|
"version": "7.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.3.1.tgz",
|
||||||
|
"integrity": "sha512-YpO13776h3e6Wy8dl2J8T9Qwlvopr+b4trCEhHE+yek6yIqV8sx6g3KozdHMbXeBpjosbPi+Ii5Z7X9oXFHUKA==",
|
||||||
|
"requires": {
|
||||||
|
"core-js": "^2.5.7",
|
||||||
|
"regenerator-runtime": "^0.12.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"regenerator-runtime": {
|
||||||
|
"version": "0.12.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
|
||||||
|
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@babel/template": {
|
"@babel/template": {
|
||||||
"version": "7.2.2",
|
"version": "7.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz",
|
||||||
|
@ -2119,9 +2135,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"bignumber.js": {
|
"bignumber.js": {
|
||||||
"version": "8.0.1",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-8.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-8.0.2.tgz",
|
||||||
"integrity": "sha512-zAySveTJXkgLYCBi0b14xzfnOs+f3G6x36I8w2a1+PFQpWk/dp0mI0F+ZZK2bu+3ELewDcSyP+Cfq++NcHX7sg=="
|
"integrity": "sha512-EiuvFrnbv0jFixEQ9f58jo7X0qI2lNGIr/MxntmVzQc5JUweDSh8y8hbTCAomFtqwUPIOWcLXP0VEOSZTG7FFw=="
|
||||||
},
|
},
|
||||||
"binary-extensions": {
|
"binary-extensions": {
|
||||||
"version": "1.12.0",
|
"version": "1.12.0",
|
||||||
|
@ -13875,9 +13891,12 @@
|
||||||
"integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ=="
|
"integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ=="
|
||||||
},
|
},
|
||||||
"xregexp": {
|
"xregexp": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.2.4.tgz",
|
||||||
"integrity": "sha512-IyMa7SVe9FyT4WbQVW3b95mTLVceHhLEezQ02+QMvmIqDnKTxk0MLWIQPSW2MXAr1zQb+9yvwYhcyQULneh3wA=="
|
"integrity": "sha512-sO0bYdYeJAJBcJA8g7MJJX7UrOZIfJPd8U2SC7B2Dd/J24U0aQNoGp33shCaBSWeb0rD5rh6VBUIXOkGal1TZA==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime-corejs2": "^7.2.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"xtend": {
|
"xtend": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "cyberchef",
|
"name": "cyberchef",
|
||||||
"version": "8.23.0",
|
"version": "8.24.1",
|
||||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||||
"author": "n1474335 <n1474335@gmail.com>",
|
"author": "n1474335 <n1474335@gmail.com>",
|
||||||
"homepage": "https://gchq.github.io/CyberChef",
|
"homepage": "https://gchq.github.io/CyberChef",
|
||||||
|
@ -82,7 +82,7 @@
|
||||||
"babel-plugin-transform-builtin-extend": "1.1.2",
|
"babel-plugin-transform-builtin-extend": "1.1.2",
|
||||||
"babel-polyfill": "^6.26.0",
|
"babel-polyfill": "^6.26.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"bignumber.js": "^8.0.1",
|
"bignumber.js": "^8.0.2",
|
||||||
"bootstrap-colorpicker": "^2.5.3",
|
"bootstrap-colorpicker": "^2.5.3",
|
||||||
"bootstrap-material-design": "^4.1.1",
|
"bootstrap-material-design": "^4.1.1",
|
||||||
"bson": "^4.0.1",
|
"bson": "^4.0.1",
|
||||||
|
@ -133,7 +133,7 @@
|
||||||
"vkbeautify": "^0.99.3",
|
"vkbeautify": "^0.99.3",
|
||||||
"xmldom": "^0.1.27",
|
"xmldom": "^0.1.27",
|
||||||
"xpath": "0.0.27",
|
"xpath": "0.0.27",
|
||||||
"xregexp": "^4.2.0",
|
"xregexp": "^4.2.4",
|
||||||
"zlibjs": "^0.3.1"
|
"zlibjs": "^0.3.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -168,7 +168,7 @@ class Dish {
|
||||||
this.value = Array.prototype.slice.call(new Uint8Array(this.value));
|
this.value = Array.prototype.slice.call(new Uint8Array(this.value));
|
||||||
break;
|
break;
|
||||||
case Dish.BIG_NUMBER:
|
case Dish.BIG_NUMBER:
|
||||||
this.value = this.value instanceof BigNumber ? Utils.strToByteArray(this.value.toFixed()) : [];
|
this.value = BigNumber.isBigNumber(this.value) ? Utils.strToByteArray(this.value.toFixed()) : [];
|
||||||
break;
|
break;
|
||||||
case Dish.JSON:
|
case Dish.JSON:
|
||||||
this.value = this.value ? Utils.strToByteArray(JSON.stringify(this.value, null, 4)) : [];
|
this.value = this.value ? Utils.strToByteArray(JSON.stringify(this.value, null, 4)) : [];
|
||||||
|
@ -265,7 +265,7 @@ class Dish {
|
||||||
case Dish.ARRAY_BUFFER:
|
case Dish.ARRAY_BUFFER:
|
||||||
return this.value instanceof ArrayBuffer;
|
return this.value instanceof ArrayBuffer;
|
||||||
case Dish.BIG_NUMBER:
|
case Dish.BIG_NUMBER:
|
||||||
return this.value instanceof BigNumber;
|
return BigNumber.isBigNumber(this.value);
|
||||||
case Dish.JSON:
|
case Dish.JSON:
|
||||||
// All values can be serialised in some manner, so we return true in all cases
|
// All values can be serialised in some manner, so we return true in all cases
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -23,6 +23,7 @@ class Operation {
|
||||||
this._breakpoint = false;
|
this._breakpoint = false;
|
||||||
this._disabled = false;
|
this._disabled = false;
|
||||||
this._flowControl = false;
|
this._flowControl = false;
|
||||||
|
this._manualBake = false;
|
||||||
this._ingList = [];
|
this._ingList = [];
|
||||||
|
|
||||||
// Public fields
|
// Public fields
|
||||||
|
@ -282,6 +283,7 @@ class Operation {
|
||||||
return this._flowControl;
|
return this._flowControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set whether this Operation is a flowcontrol op.
|
* Set whether this Operation is a flowcontrol op.
|
||||||
*
|
*
|
||||||
|
@ -291,6 +293,26 @@ class Operation {
|
||||||
this._flowControl = !!value;
|
this._flowControl = !!value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this Operation should not trigger AutoBake.
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
get manualBake() {
|
||||||
|
return this._manualBake;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether this Operation should trigger AutoBake.
|
||||||
|
*
|
||||||
|
* @param {boolean} value
|
||||||
|
*/
|
||||||
|
set manualBake(value) {
|
||||||
|
this._manualBake = !!value;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Operation;
|
export default Operation;
|
||||||
|
|
|
@ -155,6 +155,7 @@
|
||||||
"name": "Networking",
|
"name": "Networking",
|
||||||
"ops": [
|
"ops": [
|
||||||
"HTTP request",
|
"HTTP request",
|
||||||
|
"DNS over HTTPS",
|
||||||
"Strip HTTP headers",
|
"Strip HTTP headers",
|
||||||
"Dechunk HTTP response",
|
"Dechunk HTTP response",
|
||||||
"Parse User Agent",
|
"Parse User Agent",
|
||||||
|
|
|
@ -41,6 +41,7 @@ for (const opObj in Ops) {
|
||||||
inputType: op.inputType,
|
inputType: op.inputType,
|
||||||
outputType: op.presentType,
|
outputType: op.presentType,
|
||||||
flowControl: op.flowControl,
|
flowControl: op.flowControl,
|
||||||
|
manualBake: op.manualBake,
|
||||||
args: op.args
|
args: op.args
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
|
||||||
if (inDelim === null) {
|
if (inDelim === null) {
|
||||||
throw new OperationError("Unable to detect the input delimiter automatically.");
|
throw new OperationError("Unable to detect the input delimiter automatically.");
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!inDelim.includes("Direction")) {
|
||||||
// Convert the delimiter argument value to the actual character
|
// Convert the delimiter argument value to the actual character
|
||||||
inDelim = realDelim(inDelim);
|
inDelim = realDelim(inDelim);
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,16 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
|
||||||
outDelim = realDelim(outDelim);
|
outDelim = realDelim(outDelim);
|
||||||
|
|
||||||
if (!NO_CHANGE.includes(inFormat)) {
|
if (!NO_CHANGE.includes(inFormat)) {
|
||||||
|
if (inDelim.includes("Direction")) {
|
||||||
|
// Split on directions
|
||||||
|
split = input.split(/[NnEeSsWw]/g);
|
||||||
|
if (split[0] === "") {
|
||||||
|
// Remove first element if direction preceding
|
||||||
|
split = split.slice(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
split = input.split(inDelim);
|
split = input.split(inDelim);
|
||||||
|
}
|
||||||
// Replace any co-ordinate symbols with spaces so we can split on them later
|
// Replace any co-ordinate symbols with spaces so we can split on them later
|
||||||
for (let i = 0; i < split.length; i++) {
|
for (let i = 0; i < split.length; i++) {
|
||||||
split[i] = split[i].replace(/[°˝´'"]/g, " ");
|
split[i] = split[i].replace(/[°˝´'"]/g, " ");
|
||||||
|
@ -196,7 +205,7 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
|
||||||
if (inFormat.includes("Degrees")) {
|
if (inFormat.includes("Degrees")) {
|
||||||
// If the input string contains directions, we need to check if they're S or W.
|
// If the input string contains directions, we need to check if they're S or W.
|
||||||
// If either of the directions are, we should make the decimal value negative
|
// If either of the directions are, we should make the decimal value negative
|
||||||
const dirs = input.match(/[NnEeSsWw]/g);
|
const dirs = input.toUpperCase().match(/[NESW]/g);
|
||||||
if (dirs && dirs.length >= 1) {
|
if (dirs && dirs.length >= 1) {
|
||||||
// Make positive lat/lon values with S/W directions into negative values
|
// Make positive lat/lon values with S/W directions into negative values
|
||||||
if (dirs[0] === "S" || dirs[0] === "W" && latlon.lat > 0) {
|
if (dirs[0] === "S" || dirs[0] === "W" && latlon.lat > 0) {
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
/**
|
||||||
|
* @author h345983745
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
import Operation from "../Operation";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DNS over HTTPS operation
|
||||||
|
*/
|
||||||
|
class DNSOverHTTPS extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DNSOverHTTPS constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "DNS over HTTPS";
|
||||||
|
this.module = "Default";
|
||||||
|
this.description = [
|
||||||
|
"Takes a single domain name and performs a DNS lookup using DNS over HTTPS.",
|
||||||
|
"<br><br>",
|
||||||
|
"By default, <a href='https://developers.cloudflare.com/1.1.1.1/dns-over-https/'>Cloudflare</a> and <a href='https://developers.google.com/speed/public-dns/docs/dns-over-https'>Google</a> DNS over HTTPS services are supported.",
|
||||||
|
"<br><br>",
|
||||||
|
"Can be used with any service that supports the GET parameters <code>name</code> and <code>type</code>."
|
||||||
|
].join("\n");
|
||||||
|
this.infoURL = "https://wikipedia.org/wiki/DNS_over_HTTPS";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "JSON";
|
||||||
|
this.manualBake = true;
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Resolver",
|
||||||
|
type: "editableOption",
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
name: "Google",
|
||||||
|
value: "https://dns.google.com/resolve"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Cloudflare",
|
||||||
|
value: "https://cloudflare-dns.com/dns-query"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Request Type",
|
||||||
|
type: "option",
|
||||||
|
value: [
|
||||||
|
"A",
|
||||||
|
"AAAA",
|
||||||
|
"TXT",
|
||||||
|
"MX",
|
||||||
|
"DNSKEY",
|
||||||
|
"NS"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Answer Data Only",
|
||||||
|
type: "boolean",
|
||||||
|
value: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Validate DNSSEC",
|
||||||
|
type: "boolean",
|
||||||
|
value: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {JSON}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
const [resolver, requestType, justAnswer, DNSSEC] = args;
|
||||||
|
let url = URL;
|
||||||
|
try {
|
||||||
|
url = new URL(resolver);
|
||||||
|
} catch (error) {
|
||||||
|
throw new OperationError(error.toString() +
|
||||||
|
"\n\nThis error could be caused by one of the following:\n" +
|
||||||
|
" - An invalid Resolver URL\n");
|
||||||
|
}
|
||||||
|
const params = {name: input, type: requestType, cd: DNSSEC};
|
||||||
|
|
||||||
|
url.search = new URLSearchParams(params);
|
||||||
|
|
||||||
|
return fetch(url, {headers: {"accept": "application/dns-json"}}).then(response => {
|
||||||
|
return response.json();
|
||||||
|
}).then(data => {
|
||||||
|
if (justAnswer) {
|
||||||
|
return extractData(data.Answer);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}).catch(e => {
|
||||||
|
throw new OperationError(`Error making request to ${url}\n${e.toString()}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an array of just data from a DNS Answer section
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {JSON} data
|
||||||
|
* @returns {JSON}
|
||||||
|
*/
|
||||||
|
function extractData(data) {
|
||||||
|
if (typeof(data) == "undefined"){
|
||||||
|
return [];
|
||||||
|
} else {
|
||||||
|
const dataValues = [];
|
||||||
|
data.forEach(element => {
|
||||||
|
dataValues.push(element.data);
|
||||||
|
});
|
||||||
|
return dataValues;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DNSOverHTTPS;
|
|
@ -43,7 +43,7 @@ class Divide extends Operation {
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const val = div(createNumArray(input, args[0]));
|
const val = div(createNumArray(input, args[0]));
|
||||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
return BigNumber.isBigNumber(val) ? val : new BigNumber(NaN);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ class Mean extends Operation {
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const val = mean(createNumArray(input, args[0]));
|
const val = mean(createNumArray(input, args[0]));
|
||||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
return BigNumber.isBigNumber(val) ? val : new BigNumber(NaN);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ class Median extends Operation {
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const val = median(createNumArray(input, args[0]));
|
const val = median(createNumArray(input, args[0]));
|
||||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
return BigNumber.isBigNumber(val) ? val : new BigNumber(NaN);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ class Multiply extends Operation {
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const val = multi(createNumArray(input, args[0]));
|
const val = multi(createNumArray(input, args[0]));
|
||||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
return BigNumber.isBigNumber(val) ? val : new BigNumber(NaN);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,7 +240,7 @@ function regexHighlight (input, regex, displayTotal) {
|
||||||
if (groups.length) {
|
if (groups.length) {
|
||||||
title += "Groups:\n";
|
title += "Groups:\n";
|
||||||
for (let i = 0; i < groups.length; i++) {
|
for (let i = 0; i < groups.length; i++) {
|
||||||
title += `\t${i+1}: ${Utils.escapeHtml(groups[i])}\n`;
|
title += `\t${i+1}: ${Utils.escapeHtml(groups[i] || "")}\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ class StandardDeviation extends Operation {
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const val = stdDev(createNumArray(input, args[0]));
|
const val = stdDev(createNumArray(input, args[0]));
|
||||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
return BigNumber.isBigNumber(val) ? val : new BigNumber(NaN);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ class Subtract extends Operation {
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const val = sub(createNumArray(input, args[0]));
|
const val = sub(createNumArray(input, args[0]));
|
||||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
return BigNumber.isBigNumber(val) ? val : new BigNumber(NaN);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ class Sum extends Operation {
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const val = sum(createNumArray(input, args[0]));
|
const val = sum(createNumArray(input, args[0]));
|
||||||
return val instanceof BigNumber ? val : new BigNumber(NaN);
|
return BigNumber.isBigNumber(val) ? val : new BigNumber(NaN);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ class ToTable extends Operation {
|
||||||
const [cellDelims, rowDelims, firstRowHeader, format] = args;
|
const [cellDelims, rowDelims, firstRowHeader, format] = args;
|
||||||
|
|
||||||
// Process the input into a nested array of elements.
|
// Process the input into a nested array of elements.
|
||||||
const tableData = Utils.parseCSV(input, cellDelims.split(""), rowDelims.split(""));
|
const tableData = Utils.parseCSV(Utils.escapeHtml(input), cellDelims.split(""), rowDelims.split(""));
|
||||||
|
|
||||||
if (!tableData.length) return "";
|
if (!tableData.length) return "";
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
* @license Apache-2.0
|
* @license Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import Utils from "../core/Utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object to handle the creation of operation ingredients.
|
* Object to handle the creation of operation ingredients.
|
||||||
*/
|
*/
|
||||||
|
@ -156,7 +158,7 @@ class HTMLIngredient {
|
||||||
} else if ((m = this.value[i].name.match(/\[\/([a-z0-9 -()^]+)\]/i))) {
|
} else if ((m = this.value[i].name.match(/\[\/([a-z0-9 -()^]+)\]/i))) {
|
||||||
html += "</optgroup>";
|
html += "</optgroup>";
|
||||||
} else {
|
} else {
|
||||||
html += `<option populate-value="${this.value[i].value}">${this.value[i].name}</option>`;
|
html += `<option populate-value="${Utils.escapeHtml(this.value[i].value)}">${this.value[i].name}</option>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
html += `</select>
|
html += `</select>
|
||||||
|
|
|
@ -243,14 +243,22 @@ class InputWaiter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
this.closeFile();
|
this.loadFile(file);
|
||||||
this.loaderWorker = new LoaderWorker();
|
|
||||||
this.loaderWorker.addEventListener("message", this.handleLoaderMessage.bind(this));
|
|
||||||
this.loaderWorker.postMessage({"file": file});
|
|
||||||
this.set(file);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for open input button events
|
||||||
|
* Loads the opened data into the input textarea
|
||||||
|
*
|
||||||
|
* @param {event} e
|
||||||
|
*/
|
||||||
|
inputOpen(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const file = e.srcElement.files[0];
|
||||||
|
this.loadFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for messages sent back by the LoaderWorker.
|
* Handler for messages sent back by the LoaderWorker.
|
||||||
|
@ -306,6 +314,22 @@ class InputWaiter {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a file into the input.
|
||||||
|
*
|
||||||
|
* @param {File} file
|
||||||
|
*/
|
||||||
|
loadFile(file) {
|
||||||
|
if (file) {
|
||||||
|
this.closeFile();
|
||||||
|
this.loaderWorker = new LoaderWorker();
|
||||||
|
this.loaderWorker.addEventListener("message", this.handleLoaderMessage.bind(this));
|
||||||
|
this.loaderWorker.postMessage({"file": file});
|
||||||
|
this.set(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for clear IO events.
|
* Handler for clear IO events.
|
||||||
* Resets the input, output and info areas.
|
* Resets the input, output and info areas.
|
||||||
|
|
|
@ -146,6 +146,7 @@ class Manager {
|
||||||
this.addMultiEventListener("#input-text", "paste", this.input.inputPaste, this.input);
|
this.addMultiEventListener("#input-text", "paste", this.input.inputPaste, this.input);
|
||||||
document.getElementById("reset-layout").addEventListener("click", this.app.resetLayout.bind(this.app));
|
document.getElementById("reset-layout").addEventListener("click", this.app.resetLayout.bind(this.app));
|
||||||
document.getElementById("clr-io").addEventListener("click", this.input.clearIoClick.bind(this.input));
|
document.getElementById("clr-io").addEventListener("click", this.input.clearIoClick.bind(this.input));
|
||||||
|
this.addListeners("#open-file", "change", this.input.inputOpen, this.input);
|
||||||
this.addListeners("#input-text,#input-file", "dragover", this.input.inputDragover, this.input);
|
this.addListeners("#input-text,#input-file", "dragover", this.input.inputDragover, this.input);
|
||||||
this.addListeners("#input-text,#input-file", "dragleave", this.input.inputDragleave, this.input);
|
this.addListeners("#input-text,#input-file", "dragleave", this.input.inputDragleave, this.input);
|
||||||
this.addListeners("#input-text,#input-file", "drop", this.input.inputDrop, this.input);
|
this.addListeners("#input-text,#input-file", "drop", this.input.inputDrop, this.input);
|
||||||
|
|
|
@ -478,7 +478,7 @@ class OutputWaiter {
|
||||||
*/
|
*/
|
||||||
showMagicButton(opSequence, result, recipeConfig) {
|
showMagicButton(opSequence, result, recipeConfig) {
|
||||||
const magicButton = document.getElementById("magic");
|
const magicButton = document.getElementById("magic");
|
||||||
magicButton.setAttribute("data-original-title", `<i>${opSequence}</i> will produce <span class="data-text">"${Utils.truncate(result, 30)}"</span>`);
|
magicButton.setAttribute("data-original-title", `<i>${opSequence}</i> will produce <span class="data-text">"${Utils.escapeHtml(Utils.truncate(result), 30)}"</span>`);
|
||||||
magicButton.setAttribute("data-recipe", JSON.stringify(recipeConfig), null, "");
|
magicButton.setAttribute("data-recipe", JSON.stringify(recipeConfig), null, "");
|
||||||
magicButton.classList.remove("hidden");
|
magicButton.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,6 +225,10 @@
|
||||||
<div class="title no-select">
|
<div class="title no-select">
|
||||||
<label for="input-text">Input</label>
|
<label for="input-text">Input</label>
|
||||||
<span class="float-right">
|
<span class="float-right">
|
||||||
|
<button type="button" class="btn btn-primary bmd-btn-icon" id="btn-open-file" data-toggle="tooltip" title="Open file as input" onclick="document.getElementById('open-file').click();">
|
||||||
|
<i class="material-icons">input</i>
|
||||||
|
<input type="file" id="open-file" style="display: none">
|
||||||
|
</button>
|
||||||
<button type="button" class="btn btn-primary bmd-btn-icon" id="clr-io" data-toggle="tooltip" title="Clear input and output">
|
<button type="button" class="btn btn-primary bmd-btn-icon" id="clr-io" data-toggle="tooltip" title="Clear input and output">
|
||||||
<i class="material-icons">delete</i>
|
<i class="material-icons">delete</i>
|
||||||
</button>
|
</button>
|
||||||
|
|
Loading…
Reference in New Issue