From 283d7f2159db862f0b17d2840251775f6c762c6b Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 18 Dec 2018 10:40:18 +0000 Subject: [PATCH 001/147] Add Output Filter option to Magic operation --- src/core/lib/Magic.mjs | 9 ++++++--- src/core/operations/Magic.mjs | 9 +++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/core/lib/Magic.mjs b/src/core/lib/Magic.mjs index b4d5a7b0..52908eb3 100644 --- a/src/core/lib/Magic.mjs +++ b/src/core/lib/Magic.mjs @@ -267,7 +267,7 @@ class Magic { * @param {boolean} [useful=false] - Whether the current recipe should be scored highly * @returns {Object[]} - A sorted list of the recipes most likely to result in correct decoding */ - async speculativeExecution(depth=0, extLang=false, intensive=false, recipeConfig=[], useful=false) { + async speculativeExecution(depth=0, extLang=false, intensive=false, recipeConfig=[], useful=false, filter=null) { if (depth < 0) return []; // Find any operations that can be run on this data @@ -305,7 +305,7 @@ class Magic { const magic = new Magic(output, this.opPatterns), speculativeResults = await magic.speculativeExecution( - depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful); + depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful, filter); results = results.concat(speculativeResults); })); @@ -331,7 +331,10 @@ class Magic { r.fileType || // A file was found r.isUTF8 || // UTF-8 was found r.matchingOps.length // A matching op was found - ) + ) && + ( + filter == null || // Either no filter was passed, or + new RegExp(filter).test(r.data)) // the filter matches the result data ); // Return a sorted list of possible recipes along with their properties diff --git a/src/core/operations/Magic.mjs b/src/core/operations/Magic.mjs index b3e15e63..bb419bab 100644 --- a/src/core/operations/Magic.mjs +++ b/src/core/operations/Magic.mjs @@ -43,6 +43,11 @@ class Magic extends Operation { "name": "Extensive language support", "type": "boolean", "value": false + }, + { + "name": "Output Filter (Regex)", + "type": "string", + "value": "" } ]; } @@ -56,10 +61,10 @@ class Magic extends Operation { */ async run(state) { const ings = state.opList[state.progress].ingValues, - [depth, intensive, extLang] = ings, + [depth, intensive, extLang, filter] = ings, dish = state.dish, magic = new MagicLib(await dish.get(Dish.ARRAY_BUFFER)), - options = await magic.speculativeExecution(depth, extLang, intensive); + options = await magic.speculativeExecution(depth, extLang, intensive, [], false, filter); // Record the current state for use when presenting this.state = state; From 367d79e82049ea6200f807f547129de72998b2a7 Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 18 Dec 2018 11:55:49 +0000 Subject: [PATCH 002/147] Fix filtering to work on all the data and not just the result snippet --- src/core/lib/Magic.mjs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/core/lib/Magic.mjs b/src/core/lib/Magic.mjs index 52908eb3..50c632b8 100644 --- a/src/core/lib/Magic.mjs +++ b/src/core/lib/Magic.mjs @@ -276,17 +276,18 @@ class Magic { let results = []; // Record the properties of the current data - results.push({ - recipe: recipeConfig, - data: this.inputStr.slice(0, 100), - languageScores: this.detectLanguage(extLang), - fileType: this.detectFileType(), - isUTF8: this.isUTF8(), - entropy: this.calcEntropy(), - matchingOps: matchingOps, - useful: useful - }); - + if (filter == null || new RegExp(filter).test(this.inputStr)){ + results.push({ + recipe: recipeConfig, + data: this.inputStr.slice(0, 100), + languageScores: this.detectLanguage(extLang), + fileType: this.detectFileType(), + isUTF8: this.isUTF8(), + entropy: this.calcEntropy(), + matchingOps: matchingOps, + useful: useful + }); + } const prevOp = recipeConfig[recipeConfig.length - 1]; // Execute each of the matching operations, then recursively call the speculativeExecution() @@ -331,10 +332,7 @@ class Magic { r.fileType || // A file was found r.isUTF8 || // UTF-8 was found r.matchingOps.length // A matching op was found - ) && - ( - filter == null || // Either no filter was passed, or - new RegExp(filter).test(r.data)) // the filter matches the result data + ) ); // Return a sorted list of possible recipes along with their properties From c8eab5d2185cf89fbfd0b7b2065dc842de15905f Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 18 Dec 2018 14:06:39 +0000 Subject: [PATCH 003/147] Commenting --- src/core/lib/Magic.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/lib/Magic.mjs b/src/core/lib/Magic.mjs index 50c632b8..eeda155a 100644 --- a/src/core/lib/Magic.mjs +++ b/src/core/lib/Magic.mjs @@ -276,6 +276,8 @@ class Magic { let results = []; // Record the properties of the current data + // Only if there either wasn't a filter provided, + // or the filter matches in the data if (filter == null || new RegExp(filter).test(this.inputStr)){ results.push({ recipe: recipeConfig, From b6bdcaa71f8f5b689c7a76e6840c351d9a983c6a Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 18 Dec 2018 14:19:52 +0000 Subject: [PATCH 004/147] Rename output filter argument to Crib --- src/core/operations/Magic.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/Magic.mjs b/src/core/operations/Magic.mjs index bb419bab..4575acf6 100644 --- a/src/core/operations/Magic.mjs +++ b/src/core/operations/Magic.mjs @@ -45,7 +45,7 @@ class Magic extends Operation { "value": false }, { - "name": "Output Filter (Regex)", + "name": "Crib (known plaintext string or regex)", "type": "string", "value": "" } From 5d4c7244e197956e967dd5ae9626309ccf9cef2c Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 18 Dec 2018 16:52:09 +0000 Subject: [PATCH 005/147] Add note about crib to operation description --- src/core/operations/Magic.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/Magic.mjs b/src/core/operations/Magic.mjs index 4575acf6..8bfdb067 100644 --- a/src/core/operations/Magic.mjs +++ b/src/core/operations/Magic.mjs @@ -23,7 +23,7 @@ class Magic extends Operation { this.name = "Magic"; this.flowControl = true; this.module = "Default"; - this.description = "The Magic operation attempts to detect various properties of the input data and suggests which operations could help to make more sense of it.

Options
Depth: If an operation appears to match the data, it will be run and the result will be analysed further. This argument controls the maximum number of levels of recursion.

Intensive mode: When this is turned on, various operations like XOR, bit rotates, and character encodings are brute-forced to attempt to detect valid data underneath. To improve performance, only the first 100 bytes of the data is brute-forced.

Extensive language support: At each stage, the relative byte frequencies of the data will be compared to average frequencies for a number of languages. The default set consists of ~40 of the most commonly used languages on the Internet. The extensive list consists of 284 languages and can result in many languages matching the data if their byte frequencies are similar."; + this.description = "The Magic operation attempts to detect various properties of the input data and suggests which operations could help to make more sense of it.

Options
Depth: If an operation appears to match the data, it will be run and the result will be analysed further. This argument controls the maximum number of levels of recursion.

Intensive mode: When this is turned on, various operations like XOR, bit rotates, and character encodings are brute-forced to attempt to detect valid data underneath. To improve performance, only the first 100 bytes of the data is brute-forced.

Extensive language support: At each stage, the relative byte frequencies of the data will be compared to average frequencies for a number of languages. The default set consists of ~40 of the most commonly used languages on the Internet. The extensive list consists of 284 languages and can result in many languages matching the data if their byte frequencies are similar.

Optionally enter a regular expression to match a string you expect to find to filter results (crib)"; this.infoURL = "https://github.com/gchq/CyberChef/wiki/Automatic-detection-of-encoded-data-using-CyberChef-Magic"; this.inputType = "ArrayBuffer"; this.outputType = "JSON"; From f600571c6d743e573be2b7966f01f67bcf6b63a1 Mon Sep 17 00:00:00 2001 From: j433866 Date: Wed, 19 Dec 2018 09:42:09 +0000 Subject: [PATCH 006/147] Fix to make the filter work when intensive mode was turned on. --- src/core/lib/Magic.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/lib/Magic.mjs b/src/core/lib/Magic.mjs index eeda155a..640979cf 100644 --- a/src/core/lib/Magic.mjs +++ b/src/core/lib/Magic.mjs @@ -265,6 +265,7 @@ class Magic { * performance) * @param {Object[]} [recipeConfig=[]] - The recipe configuration up to this point * @param {boolean} [useful=false] - Whether the current recipe should be scored highly + * @param {string} [filter=null] - The regex crib provided by the user, to filter the operation output * @returns {Object[]} - A sorted list of the recipes most likely to result in correct decoding */ async speculativeExecution(depth=0, extLang=false, intensive=false, recipeConfig=[], useful=false, filter=null) { @@ -320,7 +321,7 @@ class Magic { await Promise.all(bfEncodings.map(async enc => { const magic = new Magic(enc.data, this.opPatterns), bfResults = await magic.speculativeExecution( - depth-1, extLang, false, [...recipeConfig, enc.conf]); + depth-1, extLang, false, [...recipeConfig, enc.conf], useful, filter); results = results.concat(bfResults); })); From 952f49e2e1839d85a83d73784f0ef23444e0601b Mon Sep 17 00:00:00 2001 From: j433866 Date: Wed, 19 Dec 2018 13:27:18 +0000 Subject: [PATCH 007/147] Add qr-image to package --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 48751e21..2e20aa2d 100644 --- a/package.json +++ b/package.json @@ -113,6 +113,7 @@ "nwmatcher": "^1.4.4", "otp": "^0.1.3", "popper.js": "^1.14.4", + "qr-image": "^3.2.0", "scryptsy": "^2.0.0", "snackbarjs": "^1.1.0", "sortablejs": "^1.7.0", From 2070e1a96b2739e54e1763743d5ca82f3a7ae9bc Mon Sep 17 00:00:00 2001 From: j433866 Date: Wed, 19 Dec 2018 13:27:45 +0000 Subject: [PATCH 008/147] Add new Generate QR Code module --- src/core/config/Categories.json | 1 + src/core/operations/GenerateQRCode.mjs | 47 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/core/operations/GenerateQRCode.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index e9fe3399..d7ec0d8c 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -351,6 +351,7 @@ "Generate UUID", "Generate TOTP", "Generate HOTP", + "Generate QR Code", "Haversine distance", "Render Image", "Remove EXIF", diff --git a/src/core/operations/GenerateQRCode.mjs b/src/core/operations/GenerateQRCode.mjs new file mode 100644 index 00000000..94cb67b5 --- /dev/null +++ b/src/core/operations/GenerateQRCode.mjs @@ -0,0 +1,47 @@ +/** + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import qr from "qr-image"; + + +/** + * Generate QR Code operation + */ +class GenerateQRCode extends Operation { + + /** + * GenerateQRCode constructor + */ + constructor() { + super(); + + this.name = "Generate QR Code"; + this.module = "Default"; + this.description = "Generates a QR code from text."; + this.infoURL = "https://en.wikipedia.org/wiki/QR_code"; + this.inputType = "string"; + this.outputType = "byteArray"; + this.args = []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {File} + */ + run(input, args) { + const qrImage = new Buffer(qr.imageSync(input, { type : "png" })); + if (qrImage == null) { + return [input]; + } + return [...qrImage]; + } + +} + +export default GenerateQRCode; From 866dd546c8bddc20a6678b0cd87d52f99714cda9 Mon Sep 17 00:00:00 2001 From: Anthony Arnold Date: Thu, 20 Dec 2018 00:05:10 +1000 Subject: [PATCH 009/147] Add the Play Media operation and place it under a new "Multimedia" category. Move the Render Image to this new category as well. --- src/core/config/Categories.json | 8 ++- src/core/operations/PlayMedia.mjs | 101 ++++++++++++++++++++++++++++++ test/index.mjs | 1 + test/tests/operations/Media.mjs | 43 +++++++++++++ 4 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/core/operations/PlayMedia.mjs create mode 100644 test/tests/operations/Media.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 3998d7db..690fea57 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -341,6 +341,13 @@ "From MessagePack" ] }, + { + "name": "Multimedia", + "ops": [ + "Render Image", + "Play Media" + ] + }, { "name": "Other", "ops": [ @@ -355,7 +362,6 @@ "Generate TOTP", "Generate HOTP", "Haversine distance", - "Render Image", "Remove EXIF", "Extract EXIF", "Numberwang", diff --git a/src/core/operations/PlayMedia.mjs b/src/core/operations/PlayMedia.mjs new file mode 100644 index 00000000..e6ac53e4 --- /dev/null +++ b/src/core/operations/PlayMedia.mjs @@ -0,0 +1,101 @@ +/** + * @author anthony-arnold [anthony.arnold@uqconnect.edu.au] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import { fromBase64, toBase64 } from "../lib/Base64"; +import { fromHex } from "../lib/Hex"; +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import Utils from "../Utils"; +import Magic from "../lib/Magic"; + +/** + * PlayMedia operation + */ +class PlayMedia extends Operation { + + /** + * PlayMedia constructor + */ + constructor() { + super(); + + this.name = "Play Media"; + this.module = "Media"; + this.description = "Plays the input as sound or video depending on the type."; + this.infoURL = ""; + this.inputType = "string"; + this.outputType = "byteArray"; + this.presentType = "html"; + this.args = [ + { + "name": "Input format", + "type": "option", + "value": ["Raw", "Base64", "Hex"] + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {byteArray} The multimedia data as bytes. + */ + run(input, args) { + const inputFormat = args[0]; + + if (!input.length) return []; + + // Convert input to raw bytes + switch (inputFormat) { + case "Hex": + input = fromHex(input); + break; + case "Base64": + // Don't trust the Base64 entered by the user. + // Unwrap it first, then re-encode later. + input = fromBase64(input, undefined, "byteArray"); + break; + case "Raw": + default: + input = Utils.strToByteArray(input); + break; + } + + + // Determine file type + const type = Magic.magicFileType(input); + if (!(type && /^audio|video/.test(type.mime))) { + throw new OperationError("Invalid file type"); + } + + return input; + } + + /** + * Displays an audio or video element that may be able to play the media + * file. + * @param data {byteArray} Data containing an audio or video file. + * @returns {string} Markup to display a media player. + */ + async present(data) { + if (!data.length) return ""; + + const type = Magic.magicFileType(data); + const matches = /^audio|video/.exec(type.mime); + if (!matches) { + throw new OperationError("Invalid file type"); + } + const dataURI = `data:${type.mime};base64,${toBase64(data)}`; + const element = matches[0]; + + let html = `<${element} src='${dataURI}' type='${type.mime}' controls>`; + html += "

Unsupported media type.

"; + html += ``; + return html; + } +} + +export default PlayMedia; diff --git a/test/index.mjs b/test/index.mjs index a08d9b32..c944b765 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -80,6 +80,7 @@ import "./tests/operations/ToGeohash.mjs"; import "./tests/operations/TranslateDateTimeFormat"; import "./tests/operations/Magic"; import "./tests/operations/ParseTLV"; +import "./tests/operations/Media"; let allTestsPassing = true; const testStatusCounts = { diff --git a/test/tests/operations/Media.mjs b/test/tests/operations/Media.mjs new file mode 100644 index 00000000..4d285e8d --- /dev/null +++ b/test/tests/operations/Media.mjs @@ -0,0 +1,43 @@ +/** + * Media operation tests. + * @author anthony-arnold [anthony.arnold@uqconnect.edu.au] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +import TestRegister from "../../TestRegister"; + +TestRegister.addTests([ + { + name: "Play Media: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { op: "Play Media", args: ["Raw"] } + ] + }, + { + name: "Play Media: raw wav", + input: "52494646bcaf010057415645666d74201000000001000100401f0000401f0000010008006461746198af0100818081808180818081808180818081808180818081808180818081808180818081808180818081808180818081808180818081808180818081808180818081808180818081808180818081808180818081818281807f82807f817e81808280827d8086817d80828184817f80807d847f7e7e8582837b81857e7e82867f7c7f857e7d838182837f83847b7c838082807c7d838481827e83827c7f848383807c7f7c80848085808180827e83827f7c82827c7f858183807c837b847f7f82827a85837a8287817f7882827f89837e7f877b7e808281817f7e807c827b7e7d8383838486838883837d7d7682797d847f7d868287867d83847c81807d7e818080828082828380807e7f827d8181807e828082858581808081807f7a7f81808183807d7c7f7f858180848484857f8082817e827b7e7f7f8385807f7f7d858183808081807f7f8183847f7e7d81828284857d7f7e8084868182837d7e7d7a7e7e8085837f80807b7f7b7a80827d7f817e8386838588868386868689868586838386868584807c7a78787875706e6c6a6c6d70757a7c7e7f80858b94a1a29a9996989a9e9ca09ea19caa8e554129243e4d475e7184a0ada2a4a0918179646167696f7b7a8390939ca4aab2af9c9691909aa2a3ae74321e162a4d5c506c88a5babca6a4a7a18c7a5e5c646f6a6b6d7f929a8f858182827e79849299a1abb2c7cc975b3826324447496788a5b8b6a9aaa9977b60494c586467707c949fa195938f8f847a717c88a0aab7b7d6a46045362f3c483d6089a1acb4a3a4a89570615652676f6d788f97a29c8e8992898381808393999da9bf8e515e504c4e5d4879929a90a1909e9c855c5b5f717679768b9b9f958c838c8d8c7f8782969ca797a963585f63425d60687e93818b99938c84786f7e7a7b7f86838a867f8486858899939697a7a6be6e465f6c4d465d678e948275958a867063688e8b828fa2a0988b7a7f7c737185898a8e90989c9b8c8c6e5f5a655159646f727f737382857c7f898a9aa4b1b6cac5d4957d695b403c4152", + expectedOutput: "", + recipeConfig: [ + { op: "From Hex", args: ["Space"] }, + { op: "Play Media", args: ["Raw"] } + ] + }, + { + name: "Play Media: hex ogg", + input: "4f676753000200000000000000003129000000000000642493e3011e01766f72626973000000000244ac0000000000008138010000000000b8014f676753000000000000000000003129000001000000a3565ae9102dffffffffffffffffffffffffffff2403766f726269731d000000586970682e4f7267206c6962566f726269732049203230303230373137000000000105766f726269732242435601000001009c739a318799629452892194de3968196394526929a55a4aa9a183166babbdf7de7befbdf7de7bef1d739431469552524aa99d739631471563524a89a5945642682184d662abbdf7de6befb5f6de7bef99424c29a41442084a281d538c29a494424a4a0825640e3ac61c538c52093dd65e6bccbdb6d87beda163ce39e61c534c4a6821740e3ae69c534c4a68a984524206a153d05289adf7de62ebb9a5da7bef81d0905500000100c040101ab20a00500000108aa1188a028486ac020032000004e0288ee3388ee23892623916101ab20a00000200100000c0900c4bb114cdd1244dd22ccf134dd3377dd3366d55d7755dd7755dd77520346415000001004040a719a61a20c28c6416080d590500200000004420c3140342435601000001000052243949a2e4a494520e836431492ae5a494521ec5e4514d3206a594524a29a594524a29a594520a8364394a2ae5a4945212a364314aaad4a494521ee5e4", + expectedOutput: "", + recipeConfig: [ + { op: "Play Media", args: ["Hex"] } + ] + }, + { + name: "Play Media: base64 webm", + input: "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", + expectedOutput: "", + recipeConfig: [ + { op: "Play Media", args: ["Base64"] } + ] + } +]); From ae20a951bec7c8d54ec7394e3a8a5cec704e5356 Mon Sep 17 00:00:00 2001 From: j433866 Date: Wed, 19 Dec 2018 14:43:31 +0000 Subject: [PATCH 010/147] Add customisation arguments. Add presentation of QR code in HTML. --- src/core/operations/GenerateQRCode.mjs | 69 +++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/src/core/operations/GenerateQRCode.mjs b/src/core/operations/GenerateQRCode.mjs index 94cb67b5..198a9500 100644 --- a/src/core/operations/GenerateQRCode.mjs +++ b/src/core/operations/GenerateQRCode.mjs @@ -7,7 +7,8 @@ import Operation from "../Operation"; import OperationError from "../errors/OperationError"; import qr from "qr-image"; - +import { toBase64 } from "../lib/Base64"; +import Magic from "../lib/Magic"; /** * Generate QR Code operation @@ -21,12 +22,29 @@ class GenerateQRCode extends Operation { super(); this.name = "Generate QR Code"; - this.module = "Default"; + this.module = "QRCode"; this.description = "Generates a QR code from text."; this.infoURL = "https://en.wikipedia.org/wiki/QR_code"; this.inputType = "string"; this.outputType = "byteArray"; - this.args = []; + this.presentType = "html"; + this.args = [ + { + "name": "Image Format", + "type": "option", + "value": ["SVG", "PNG"] + }, + { + "name": "Size of QR module", + "type": "number", + "value": 5 + }, + { + "name": "Margin", + "type": "number", + "value": 2 + } + ]; } /** @@ -35,11 +53,50 @@ class GenerateQRCode extends Operation { * @returns {File} */ run(input, args) { - const qrImage = new Buffer(qr.imageSync(input, { type : "png" })); + // Create new QR image from the input data, and convert it to a buffer + const [format, size, margin] = args; + const qrImage = qr.imageSync(input, { type: format, size: size, margin: margin }); if (qrImage == null) { - return [input]; + throw new OperationError("Error generating QR code."); + } + if (format === "SVG") { + return [...Buffer.from(qrImage)]; + } else if (format === "PNG") { + // Return the QR image buffer as a byte array + return [...qrImage]; + } else { + throw new OperationError("Error generating QR code."); + } + } + + /** + * Displays the QR image using HTML for web apps + * + * @param {byteArray} data + * @returns {html} + */ + present(data, args) { + if (!data.length) return ""; + + const [format] = args; + if (format === "SVG") { + let outputData = ""; + for (let i = 0; i < data.length; i++){ + outputData += String.fromCharCode(parseInt(data[i])); + } + return outputData; + } else if (format === "PNG") { + let dataURI = "data:"; + const type = Magic.magicFileType(data); + if (type && type.mime.indexOf("image") === 0){ + dataURI += type.mime + ";"; + } else { + throw new OperationError("Invalid file type"); + } + dataURI += "base64," + toBase64(data); + + return ""; } - return [...qrImage]; } } From ed2c886359669ee090c5e795668510c9f8a19f14 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 19 Dec 2018 17:24:38 +0000 Subject: [PATCH 011/147] Magic crib now only applies after all branches have been explored. --- src/core/lib/Magic.mjs | 33 +++++++++++++++------------------ src/core/operations/Magic.mjs | 12 +++++++++--- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/core/lib/Magic.mjs b/src/core/lib/Magic.mjs index 640979cf..00132d5d 100644 --- a/src/core/lib/Magic.mjs +++ b/src/core/lib/Magic.mjs @@ -265,10 +265,10 @@ class Magic { * performance) * @param {Object[]} [recipeConfig=[]] - The recipe configuration up to this point * @param {boolean} [useful=false] - Whether the current recipe should be scored highly - * @param {string} [filter=null] - The regex crib provided by the user, to filter the operation output + * @param {string} [crib=null] - The regex crib provided by the user, for filtering the operation output * @returns {Object[]} - A sorted list of the recipes most likely to result in correct decoding */ - async speculativeExecution(depth=0, extLang=false, intensive=false, recipeConfig=[], useful=false, filter=null) { + async speculativeExecution(depth=0, extLang=false, intensive=false, recipeConfig=[], useful=false, crib=null) { if (depth < 0) return []; // Find any operations that can be run on this data @@ -277,20 +277,17 @@ class Magic { let results = []; // Record the properties of the current data - // Only if there either wasn't a filter provided, - // or the filter matches in the data - if (filter == null || new RegExp(filter).test(this.inputStr)){ - results.push({ - recipe: recipeConfig, - data: this.inputStr.slice(0, 100), - languageScores: this.detectLanguage(extLang), - fileType: this.detectFileType(), - isUTF8: this.isUTF8(), - entropy: this.calcEntropy(), - matchingOps: matchingOps, - useful: useful - }); - } + results.push({ + recipe: recipeConfig, + data: this.inputStr.slice(0, 100), + languageScores: this.detectLanguage(extLang), + fileType: this.detectFileType(), + isUTF8: this.isUTF8(), + entropy: this.calcEntropy(), + matchingOps: matchingOps, + useful: useful, + matchesCrib: crib && crib.test(this.inputStr) + }); const prevOp = recipeConfig[recipeConfig.length - 1]; // Execute each of the matching operations, then recursively call the speculativeExecution() @@ -309,7 +306,7 @@ class Magic { const magic = new Magic(output, this.opPatterns), speculativeResults = await magic.speculativeExecution( - depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful, filter); + depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful, crib); results = results.concat(speculativeResults); })); @@ -321,7 +318,7 @@ class Magic { await Promise.all(bfEncodings.map(async enc => { const magic = new Magic(enc.data, this.opPatterns), bfResults = await magic.speculativeExecution( - depth-1, extLang, false, [...recipeConfig, enc.conf], useful, filter); + depth-1, extLang, false, [...recipeConfig, enc.conf], false, crib); results = results.concat(bfResults); })); diff --git a/src/core/operations/Magic.mjs b/src/core/operations/Magic.mjs index 8bfdb067..1555ebad 100644 --- a/src/core/operations/Magic.mjs +++ b/src/core/operations/Magic.mjs @@ -23,7 +23,7 @@ class Magic extends Operation { this.name = "Magic"; this.flowControl = true; this.module = "Default"; - this.description = "The Magic operation attempts to detect various properties of the input data and suggests which operations could help to make more sense of it.

Options
Depth: If an operation appears to match the data, it will be run and the result will be analysed further. This argument controls the maximum number of levels of recursion.

Intensive mode: When this is turned on, various operations like XOR, bit rotates, and character encodings are brute-forced to attempt to detect valid data underneath. To improve performance, only the first 100 bytes of the data is brute-forced.

Extensive language support: At each stage, the relative byte frequencies of the data will be compared to average frequencies for a number of languages. The default set consists of ~40 of the most commonly used languages on the Internet. The extensive list consists of 284 languages and can result in many languages matching the data if their byte frequencies are similar.

Optionally enter a regular expression to match a string you expect to find to filter results (crib)"; + this.description = "The Magic operation attempts to detect various properties of the input data and suggests which operations could help to make more sense of it.

Options
Depth: If an operation appears to match the data, it will be run and the result will be analysed further. This argument controls the maximum number of levels of recursion.

Intensive mode: When this is turned on, various operations like XOR, bit rotates, and character encodings are brute-forced to attempt to detect valid data underneath. To improve performance, only the first 100 bytes of the data is brute-forced.

Extensive language support: At each stage, the relative byte frequencies of the data will be compared to average frequencies for a number of languages. The default set consists of ~40 of the most commonly used languages on the Internet. The extensive list consists of 284 languages and can result in many languages matching the data if their byte frequencies are similar.

Optionally enter a regular expression to match a string you expect to find to filter results (crib)."; this.infoURL = "https://github.com/gchq/CyberChef/wiki/Automatic-detection-of-encoded-data-using-CyberChef-Magic"; this.inputType = "ArrayBuffer"; this.outputType = "JSON"; @@ -61,10 +61,16 @@ class Magic extends Operation { */ async run(state) { const ings = state.opList[state.progress].ingValues, - [depth, intensive, extLang, filter] = ings, + [depth, intensive, extLang, crib] = ings, dish = state.dish, magic = new MagicLib(await dish.get(Dish.ARRAY_BUFFER)), - options = await magic.speculativeExecution(depth, extLang, intensive, [], false, filter); + cribRegex = (crib && crib.length) ? new RegExp(crib, "i") : null; + let options = await magic.speculativeExecution(depth, extLang, intensive, [], false, cribRegex); + + // Filter down to results which matched the crib + if (cribRegex) { + options = options.filter(option => option.matchesCrib); + } // Record the current state for use when presenting this.state = state; From f367c1f78bbcecd9f841efa31622627758bbe041 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 19 Dec 2018 17:25:05 +0000 Subject: [PATCH 012/147] 8.15.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5e20c8bc..8ea5bd80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.15.0", + "version": "8.15.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 3505cfa2..e5cd12e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.15.0", + "version": "8.15.1", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 01c4cfdc8d6fbeb9589e820aa2414f5736a9eeb2 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 19 Dec 2018 17:58:38 +0000 Subject: [PATCH 013/147] Tidied up 'Play Media' operation --- CHANGELOG.md | 6 ++++++ src/core/config/Categories.json | 10 +++++----- src/core/operations/PlayMedia.mjs | 9 +++++---- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6355831e..751ac287 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ 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.16.0] - 2018-12-19 +- 'Play Media' operation added [@anthony-arnold] | [#446] + ### [8.15.0] - 2018-12-18 - 'Text Encoding Brute Force' operation added [@Cynser] | [#439] @@ -76,6 +79,7 @@ All major and minor version changes will be documented in this file. Details of +[8.16.0]: https://github.com/gchq/CyberChef/releases/tag/v8.16.0 [8.15.0]: https://github.com/gchq/CyberChef/releases/tag/v8.15.0 [8.14.0]: https://github.com/gchq/CyberChef/releases/tag/v8.14.0 [8.13.0]: https://github.com/gchq/CyberChef/releases/tag/v8.13.0 @@ -112,6 +116,7 @@ All major and minor version changes will be documented in this file. Details of [@jarmovanlenthe]: https://github.com/jarmovanlenthe [@tcode2k16]: https://github.com/tcode2k16 [@Cynser]: https://github.com/Cynser +[@anthony-arnold]: https://github.com/anthony-arnold [#95]: https://github.com/gchq/CyberChef/pull/299 [#173]: https://github.com/gchq/CyberChef/pull/173 @@ -138,3 +143,4 @@ All major and minor version changes will be documented in this file. Details of [#439]: https://github.com/gchq/CyberChef/pull/439 [#441]: https://github.com/gchq/CyberChef/pull/441 [#443]: https://github.com/gchq/CyberChef/pull/443 +[#446]: https://github.com/gchq/CyberChef/pull/446 diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 690fea57..2e6bfe2d 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -342,8 +342,12 @@ ] }, { - "name": "Multimedia", + "name": "Forensics", "ops": [ + "Detect File Type", + "Scan for Embedded Files", + "Remove EXIF", + "Extract EXIF", "Render Image", "Play Media" ] @@ -354,16 +358,12 @@ "Entropy", "Frequency distribution", "Chi Square", - "Detect File Type", - "Scan for Embedded Files", "Disassemble x86", "Pseudo-Random Number Generator", "Generate UUID", "Generate TOTP", "Generate HOTP", "Haversine distance", - "Remove EXIF", - "Extract EXIF", "Numberwang", "XKCD Random Number" ] diff --git a/src/core/operations/PlayMedia.mjs b/src/core/operations/PlayMedia.mjs index e6ac53e4..81328a73 100644 --- a/src/core/operations/PlayMedia.mjs +++ b/src/core/operations/PlayMedia.mjs @@ -23,8 +23,8 @@ class PlayMedia extends Operation { super(); this.name = "Play Media"; - this.module = "Media"; - this.description = "Plays the input as sound or video depending on the type."; + this.module = "Default"; + this.description = "Plays the input as audio or video depending on the type.

Tags: sound, movie, mp3, mp4, mov, webm, wav, ogg"; this.infoURL = ""; this.inputType = "string"; this.outputType = "byteArray"; @@ -44,7 +44,7 @@ class PlayMedia extends Operation { * @returns {byteArray} The multimedia data as bytes. */ run(input, args) { - const inputFormat = args[0]; + const [inputFormat] = args; if (!input.length) return []; @@ -68,7 +68,7 @@ class PlayMedia extends Operation { // Determine file type const type = Magic.magicFileType(input); if (!(type && /^audio|video/.test(type.mime))) { - throw new OperationError("Invalid file type"); + throw new OperationError("Invalid or unrecognised file type"); } return input; @@ -77,6 +77,7 @@ class PlayMedia extends Operation { /** * Displays an audio or video element that may be able to play the media * file. + * * @param data {byteArray} Data containing an audio or video file. * @returns {string} Markup to display a media player. */ From 8c3569ea63728e1ccd8690f87a0eacde97385cc5 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 19 Dec 2018 17:59:55 +0000 Subject: [PATCH 014/147] 8.16.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8ea5bd80..f146cc0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.15.1", + "version": "8.16.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e5cd12e9..cfa83d39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.15.1", + "version": "8.16.0", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From b3113c109b1e963f4a21c949f478d2724d8c01a9 Mon Sep 17 00:00:00 2001 From: j433866 Date: Thu, 20 Dec 2018 14:42:12 +0000 Subject: [PATCH 015/147] Change module to Image, change default format value to PNG --- src/core/operations/GenerateQRCode.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/operations/GenerateQRCode.mjs b/src/core/operations/GenerateQRCode.mjs index 198a9500..2b260617 100644 --- a/src/core/operations/GenerateQRCode.mjs +++ b/src/core/operations/GenerateQRCode.mjs @@ -22,9 +22,9 @@ class GenerateQRCode extends Operation { super(); this.name = "Generate QR Code"; - this.module = "QRCode"; + this.module = "Image"; this.description = "Generates a QR code from text."; - this.infoURL = "https://en.wikipedia.org/wiki/QR_code"; + this.infoURL = "https://wikipedia.org/wiki/QR_code"; this.inputType = "string"; this.outputType = "byteArray"; this.presentType = "html"; @@ -32,7 +32,7 @@ class GenerateQRCode extends Operation { { "name": "Image Format", "type": "option", - "value": ["SVG", "PNG"] + "value": ["PNG", "SVG"] }, { "name": "Size of QR module", From 1953d9a4c8dc0db0ffb6e38dd8dcfc25acc82394 Mon Sep 17 00:00:00 2001 From: j433866 Date: Thu, 20 Dec 2018 14:42:32 +0000 Subject: [PATCH 016/147] Add Jimp and jsqr modules --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 2e20aa2d..67034936 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "exif-parser": "^0.1.12", "file-saver": "^2.0.0-rc.4", "highlight.js": "^9.13.1", + "jimp": "^0.6.0", "jquery": "^3.3.1", "js-crc": "^0.2.0", "js-sha3": "^0.8.0", @@ -99,6 +100,7 @@ "jsesc": "^2.5.1", "jsonpath": "^1.0.0", "jsonwebtoken": "^8.3.0", + "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", "lodash": "^4.17.11", From e5b2b84073784ada1b832095a16687be66542ffe Mon Sep 17 00:00:00 2001 From: j433866 Date: Thu, 20 Dec 2018 14:45:23 +0000 Subject: [PATCH 017/147] Add new ParseQRCode operation --- src/core/config/Categories.json | 1 + src/core/operations/ParseQRCode.mjs | 82 +++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 src/core/operations/ParseQRCode.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index d7ec0d8c..d876df07 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -352,6 +352,7 @@ "Generate TOTP", "Generate HOTP", "Generate QR Code", + "Parse QR Code", "Haversine distance", "Render Image", "Remove EXIF", diff --git a/src/core/operations/ParseQRCode.mjs b/src/core/operations/ParseQRCode.mjs new file mode 100644 index 00000000..6d4efa5b --- /dev/null +++ b/src/core/operations/ParseQRCode.mjs @@ -0,0 +1,82 @@ +/** + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import Magic from "../lib/Magic"; +import jsqr from "jsqr"; +import jimp from "jimp"; + +/** + * Parse QR Code operation + */ +class ParseQRCode extends Operation { + + /** + * ParseQRCode constructor + */ + constructor() { + super(); + + this.name = "Parse QR Code"; + this.module = "Image"; + this.description = "Reads an image file and attempts to detect and read a QR code from the image."; + this.infoURL = "https://wikipedia.org/wiki/QR_code"; + this.inputType = "byteArray"; + this.outputType = "string"; + this.args = []; + } + + /** + * @param {byteArray} input + * @param {Object[]} args + * @returns {string} + */ + async run(input, args) { + const type = Magic.magicFileType(input); + // Make sure that the input is an image + if (type && type.mime.indexOf("image") === 0){ + + return new Promise((resolve, reject) => { + // Read the input + jimp.read(Buffer.from(input)) + .then(image => { + image.rgba(false); // Disable RGBA (uses just RGB) + + // Get the buffer of the new image and read it in Jimp + // Don't actually need the new image buffer, just need + // Jimp to refresh the current object + image.getBuffer(image.getMIME(), (err, buffer) => { + jimp.read(buffer) + .then(newImage =>{ + // If the image has been read correctly, try to find a QR code + if (image.bitmap != null){ + const qrData = jsqr(image.bitmap.data, image.getWidth(), image.getHeight()); + if (qrData != null) { + resolve(qrData.data); + } else { + log.error(image.bitmap); + reject(new OperationError("Error parsing QR code from image.")); + } + } else { + reject(new OperationError("Error reading the image data.")); + } + }); + }); + }) + .catch(err => { + reject(new OperationError("Error opening the image. Are you sure this is an image file?")); + }); + }); + } else { + throw new OperationError("Invalid file type."); + } + + } + +} + +export default ParseQRCode; From ed25017e2d84252649aba5d8eabba54c404f30e5 Mon Sep 17 00:00:00 2001 From: j433866 Date: Thu, 20 Dec 2018 14:46:24 +0000 Subject: [PATCH 018/147] Add process.browser to webpack config, so we don't include stuff we don't need --- webpack.config.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webpack.config.js b/webpack.config.js index 3e0412be..a8a264c3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -46,6 +46,9 @@ module.exports = { raw: true, entryOnly: true }), + new webpack.DefinePlugin({ + "process.browser": "true" + }), vendorCSS, projectCSS ], From ae5128a33a6630b17c51dbf8c0c3afee8d154e61 Mon Sep 17 00:00:00 2001 From: j433866 Date: Fri, 21 Dec 2018 11:23:51 +0000 Subject: [PATCH 019/147] Always generate a PNG if the format isn't SVG --- src/core/operations/GenerateQRCode.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/GenerateQRCode.mjs b/src/core/operations/GenerateQRCode.mjs index 2b260617..d634b1bd 100644 --- a/src/core/operations/GenerateQRCode.mjs +++ b/src/core/operations/GenerateQRCode.mjs @@ -85,7 +85,7 @@ class GenerateQRCode extends Operation { outputData += String.fromCharCode(parseInt(data[i])); } return outputData; - } else if (format === "PNG") { + } else { let dataURI = "data:"; const type = Magic.magicFileType(data); if (type && type.mime.indexOf("image") === 0){ From d0e428b728b22c9a73d5c53a15d13673f3c12314 Mon Sep 17 00:00:00 2001 From: j433866 Date: Fri, 21 Dec 2018 11:24:31 +0000 Subject: [PATCH 020/147] Improve image normalising --- src/core/operations/ParseQRCode.mjs | 79 +++++++++++++++++++---------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/src/core/operations/ParseQRCode.mjs b/src/core/operations/ParseQRCode.mjs index 6d4efa5b..34bd5d71 100644 --- a/src/core/operations/ParseQRCode.mjs +++ b/src/core/operations/ParseQRCode.mjs @@ -23,11 +23,17 @@ class ParseQRCode extends Operation { this.name = "Parse QR Code"; this.module = "Image"; - this.description = "Reads an image file and attempts to detect and read a QR code from the image."; + this.description = "Reads an image file and attempts to detect and read a QR code from the image.

Normalise Image
Attempt to normalise the image before parsing it, to try and improve detection of a QR code."; this.infoURL = "https://wikipedia.org/wiki/QR_code"; this.inputType = "byteArray"; this.outputType = "string"; - this.args = []; + this.args = [ + { + "name": "Normalise image", + "type": "boolean", + "value": true + } + ]; } /** @@ -37,38 +43,55 @@ class ParseQRCode extends Operation { */ async run(input, args) { const type = Magic.magicFileType(input); + const [normalise] = args; // Make sure that the input is an image if (type && type.mime.indexOf("image") === 0){ - - return new Promise((resolve, reject) => { - // Read the input - jimp.read(Buffer.from(input)) - .then(image => { - image.rgba(false); // Disable RGBA (uses just RGB) - - // Get the buffer of the new image and read it in Jimp - // Don't actually need the new image buffer, just need - // Jimp to refresh the current object - image.getBuffer(image.getMIME(), (err, buffer) => { - jimp.read(buffer) - .then(newImage =>{ - // If the image has been read correctly, try to find a QR code - if (image.bitmap != null){ - const qrData = jsqr(image.bitmap.data, image.getWidth(), image.getHeight()); - if (qrData != null) { - resolve(qrData.data); - } else { - log.error(image.bitmap); - reject(new OperationError("Error parsing QR code from image.")); - } - } else { - reject(new OperationError("Error reading the image data.")); - } + let normalisedImage = null; + if (normalise){ + // Process the image to be easier to read by jsqr + // Disables the alpha channel + // Sets the image default background to white + // Normalises the image colours + // Makes the image greyscale + // Converts image to a JPEG + normalisedImage = await new Promise((resolve, reject) => { + jimp.read(Buffer.from(input)) + .then(image => { + image + .rgba(false) + .background(0xFFFFFFFF) + .normalize() + .greyscale() + .getBuffer(jimp.MIME_JPEG, (error, result) => { + resolve([...result]); }); + }) + .catch(err => { + reject(new OperationError("Error reading the image file.")); }); + }); + } else { + normalisedImage = input; + } + if (normalisedImage instanceof OperationError){ + return normalisedImage; + } + return new Promise((resolve, reject) => { + jimp.read(Buffer.from(normalisedImage)) + .then(image => { + if (image.bitmap != null){ + const qrData = jsqr(image.bitmap.data, image.getWidth(), image.getHeight()); + if (qrData != null){ + resolve(qrData.data); + } else { + reject(new OperationError("Couldn't read a QR code from the image.")); + } + } else { + reject(new OperationError("Error reading the normalised image file.")); + } }) .catch(err => { - reject(new OperationError("Error opening the image. Are you sure this is an image file?")); + reject(new OperationError("Error reading the normalised image file.")); }); }); } else { From c2f6b8df669b2e7f267ac12504ae279f84e61916 Mon Sep 17 00:00:00 2001 From: j433866 Date: Fri, 21 Dec 2018 11:26:03 +0000 Subject: [PATCH 021/147] Add tests for Parse QR Code module --- test/index.mjs | 1 + test/tests/operations/ParseQRCode.mjs | 71 +++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 test/tests/operations/ParseQRCode.mjs diff --git a/test/index.mjs b/test/index.mjs index 9c11f6ae..e6600f52 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -63,6 +63,7 @@ import "./tests/operations/OTP"; import "./tests/operations/PGP"; import "./tests/operations/PHP"; import "./tests/operations/ParseIPRange"; +import "./tests/operations/ParseQRCode"; import "./tests/operations/PowerSet"; import "./tests/operations/Regex"; import "./tests/operations/Register"; diff --git a/test/tests/operations/ParseQRCode.mjs b/test/tests/operations/ParseQRCode.mjs new file mode 100644 index 00000000..d1b7990c --- /dev/null +++ b/test/tests/operations/ParseQRCode.mjs @@ -0,0 +1,71 @@ +/** + * Parse QR Code tests + * + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +import TestRegister from "../../TestRegister"; + +TestRegister.addTests([ + { + name: "Parse QR Code : JPEG", + input: "ffd8ffe000104a46494600010100004800480000ffe1008c4578696600004d4d002a000000080005011200030000000100010000011a0005000000010000004a011b0005000000010000005201280003000000010002000087690004000000010000005a00000000000000480000000100000048000000010003a00100030000000100010000a002000400000001000001e0a003000400000001000001e000000000ffed003850686f746f73686f7020332e30003842494d04040000000000003842494d0425000000000010d41d8cd98f00b204e9800998ecf8427effc000110801e001e003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffdb004300020202020202030202030403030304050404040405070505050505070807070707070708080808080808080a0a0a0a0a0a0b0b0b0b0b0d0d0d0d0d0d0d0d0d0dffdb004301020202030303060303060d0907090d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0dffdd0004001effda000c03010002110311003f00fdfca28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2803ffd0fdfca28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2803ffd1fdfca28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2803ffd2fdfca28a2800a28a2800a28a2803f3c7f6e8fdba2f3f636bcf06da5a78362f15ff00c25716a72b34ba99d3becdfd9c6d86062dae37effb47fb38dbdf3c7c07ff000fc4d5bfe88f5aff00e14adffcaea4ff0082e27fc85be0f7fd7af89bff0042d3abf03a803f7cbfe1f89ab7fd11eb5ffc295bff0095d47fc3f1356ffa23d6bff852b7ff002babf0505bc8c010539f59107e99a5fb349eb1ff00dfc4ff001a00fdeaff0087e26adff447ad7ff0a56ffe5751ff000fc4d5bfe88f5aff00e14adffcaeafc15fb349eb1ffdfc4ff1a3ecd27ac7ff007f13fc6803f7abfe1f89ab7fd11eb5ff00c295bff95d47fc3f1356ff00a23d6bff00852b7ff2babf057ecd27ac7ff7f13fc68fb349eb1ffdfc4ff1a00fdeaff87e26adff00447ad7ff000a56ff00e5751ff0fc4d5bfe88f5affe14adff00caeafc15fb349eb1ff00dfc4ff001a3ecd27ac7ff7f13fc6803f7abfe1f89ab7fd11eb5ffc295bff0095d5ec5f00bfe0adbaa7c6df8bfe13f85b27c30b6d223f12ea9169cd7cbaf35d35bf9aaedbc45f618c3e3674debd6bf9ad6528db4e3f0208fcc715f61fec0fff00276ff0b7fec67b4ffd153d007f66f4514500078afc2cf891ff000592d57e1f78efc41e0c5f8516b7aba26aba8e9ab727c42d0998585dcd6de614fb03eddfe56edbb8e338c9eb5fba47a7e22bf876fda4ff00e4ba78f3fec68f107fe9d6ee803fa2afd8fbfe0a5da8fed51f17edbe175c7c3e83c371dc69f7f7bf6e8f596be606c9633b3ca36900f9bccebbf8c74afd60afe55ffe090bff002765a77fd80b5dff00d02dabfaa7cfd7f2a00fcaff00db27fe0a45a87eca1f1507c37b6f0041e268ce9561a97db24d61ac0e6f5ee5767962d271f2fd9f39ddceee9c73f39f813fe0b35ab78d7c61a37854fc26b5b41aaea36360671e22694c42f2e62b7de13ec0bbb6f99bb1919c6322be4fff0082c400ff00b51a4419439f0b686c159829204ba8824648ce0919fad7e797c0a4f27e2ff834c8d18dde21d11540914927fb46d8e00073d013401fdcd5140a2800a2933f5fca96803c9fe3afc4a93e0dfc1ef17fc54874f5d59fc2ba4dcea8b64d31b71706dd777966508e53774ddb5b1e86bf152ebfe0b7babdadccd6c7e0fda3186468c9ff008495c6769c7fd03abf56ff006e0ff9344f8b9ff629ea5ffa28d7f179ac7fc85af7febe65ff00d0cd007ef0ff00c3f1356ffa23d6bff852b7ff002baa7b7ff82deeaf70ec83e0fda8db1cb27fc8cae7fd5a33e3fe41ddf18afc0b485e4190547fbccabfcc8ad1d3a3f26791e578d57ecf723fd6275685c01d7a92702803fbd2d16fceaba4596a853cb3796d0ce501cedf3503e33c6719c56a5737e0eff914f45ffb075a7fe894ae92800afc9ffdb07fe0a5da8fecaff17ee7e175bfc3e83c491dbe9f617bf6e93596b1626f5643b3ca16938f97cbebbf9cf4afd5fcfd7f2afe567fe0af0bbff6b5d4630ca18e83a190198292025ce48c91d322803e9dff0087e26adff447ad7ff0a56ffe5751ff000fc4d5bfe88f5aff00e14adffcaeafc1436f2004929c7fd3443fd6a0a00febb3f61efdba2f7f6c4d4bc536177e0c87c283c39696174ad16a6750f3fedb2dc45b4e6dadf66cfb3e7f8b3bbb639fd10afe7cff00e0893ff21ef89bff00608d0fff004af51afe832803f15fe3effc15b754f825f17fc59f0b63f8616dabc7e1ad525d396f9b5e6b56b8f2951b798bec3204cefe9bdba578effc3f1356ff00a23d6bff00852b7ff2babf34bf6f8ff93b7f8a5ff633ddff00e8a82be3c552edb463f1200fccf1401fbe1ff0fc4d5bfe88f5affe14adff00caea3fe1f89ab7fd11eb5ffc295bff0095d5f82bf6693d63ff00bf89fe347d9a4f58ff00efe27f8d007ef57fc3f1356ffa23d6bff852b7ff002ba8ff0087e26adff447ad7ff0a56ffe5757e0afd9a4f58ffefe27f8d1f6693d63ff00bf89fe3401fbd5ff000fc4d5bfe88f5aff00e14adffcaea3fe1f89ab7fd11eb5ff00c295bff95d5f82bf6693d63ffbf89fe347d9a4f58ffefe27f8d007ef57fc3f1356ff00a23d6bff00852b7ff2ba8ff87e26adff00447ad7ff000a56ff00e5757e0afd9a4f58ff00efe27f8d21b7914124a71e9221fd33401fbd7ff0fc4d5bfe88f5affe14adff00caeafbf3f617fdba2f3f6c9bcf195a5df8362f0a7fc22916992ab45a99d47ed3fda26e460e6dadf66cfb3ffb59dddb1cff002235fbe3ff00043bff0090b7c61ffaf5f0cffe85a8d007f417451450014514500145145007ffd3fdfca28a2800a28a2800a28a2803f9f4ff0082e27fc85be0f7fd7af89bff0042d3abf056dc06908233f2487f2535fbd5ff0005c4ff0090b7c1effaf5f137fe85a757e0b5b7fac3ff005ce4ff00d00d007f693f03be07fc17bdf82fe02bdbdf0178667b8b8f0ce912cb2c9a45a3c9248f6913333318892cc49249e49af53ff850bf03bfe89e785bff0004d67ffc6a93e027fc90df879ff62b68dffa47155ff8b7f16fc0bf03fc0b7df123e245f49a7681a7496f15c5c456f35d3abdd4c9044045023c8dba4751f2a9c67278a00a3ff0a17e077fd13cf0b7fe09acff00f8d51ff0a17e077fd13cf0b7fe09acff00f8d57c99ff000f4afd8b3fe872bfff00c27f55ff00e45a3fe1e95fb167fd0e57ff00f84feabffc8b401f59ff00c285f81dff0044f3c2dff826b3ff00e3547fc285f81dff0044f3c2dff826b3ff00e355f267fc3d2bf62cff00a1caff00ff0009fd57ff009168ff0087a57ec59ff4395fff00e13faaff00f22d007d67ff000a17e077fd13cf0b7fe09acfff008d521f80bf03b07fe2de785bff0004d67ffc6abcefe06fed83f013f68dd7751f0e7c24d76e356bfd2ed16faea39b4dbcb109033f961835cc31ab65b8c2926be9c3d0d007f1c5ff000511d0b44f0e7ed71f11349f0fe9f6ba658dbea96e90db59c296f0c6a74db1721238c2aa8dcc4e00ea49ef58dfb03ffc9dbfc2dffb19ed3ff454f5d7ff00c14abfe4f23e257fd85adbff004d7a7d721fb03ffc9dbfc2dffb19ed3ff454f401fd9bd145140087a7e22bf876fda4ff00e4ba78f3fec68f107fe9d6eebfb8935fcbe7c5ff00f8263fed71e37f8a3e2df14e93e17b06b0d535fd62f6cddf59b48d9edeeefee27899909254b2480e09c8ef8390003f2bbc27e34f177817545d6fc19adea5a0ea0a8f10bad2ef26b19c24980ea258191c2b606e19c1c0cf4af4eff869cfda1ffe8a778d7ff0a4d4bff922bdafe317fc13e7f693f81be04bbf88bf10342b3b2d1ace7b5b69658754b6b97125e4ab0c40469f310646009edd4d7c3f401fd5bffc13a3c29e18f8bdfb345878cfe2c69367e36f103eb3abd9b6ade2381357d41adadae5d6189ae6ec4b3347102422962172718c9afbc22f815f04e09a2b883e1ff85e396191258dd747b30c9246c1d19488b219580208e41008af8bff00e0949ff2687a77fd8c3af7fe95b57e92d0015f92ff00f0574f1c78d3c0bf07fc17a87827c41ab787aea7f11dc4734fa45fdc69f2c91a69b7520477b778d9977a2b6d271902bf5a2bf1bbfe0b3dff00244bc0ff00f6335d7fe9aaf2803f0574efda5bf684b8d42da097e2778d4a4b3468c3fe124d4fa33007fe5e2bfae3fd8db57d5fc41fb2c7c2dd6f5ebeb9d4b51bef0c69f3dcddddcaf3dc4f2bc796792490b3bb31e4b3124d7f159a3ffc85acbfebe62ffd0c57f687fb0fff00c9a27c23ff00b14f4dff00d142800fdb83fe4d13e2e7fd8a7a97fe8a35fc5e6b1ff216bdff00af997ff4335fdb7fed49e08f12fc4afd9dbe21f803c1d6cb79adebfe1fbdb0b0b769121124f326d452f2154504f762057f37179ff04a2fdb22eaee6ba3e16d394cd23c981ae5a1037126803efdff008242fc35f877e31f837e34bcf16f85b45d6ae21f11db4714ba8e9f6f752221d32d1caab4a8c402cc588071924f5afd6b93e00fc0b951a297e1df851d1d4ab2b68b66432b0c10418b90475afca7fd92bc59a0ff00c1387c09ab7813f6b4b83e16d63c59aaff006b6910d8c336b493d95a5a5ada4923496114a232255c159029e463239afaa3fe1e95fb167fd0e57fff0084feabff00c8b401fa0d0c315bc490408b1c71a8444501555546000070001d057e7e7fc14e7c51e26f087ecaba86b3e12d6350d0f505d7745885e699773595c88e5b90aea2581d240194e0807914cff87a57ec59ff004395ff00fe13faafff0022d782fed29f1dbe1a7ededf09efbe027ecc3a94be25f1acb7763ad2595e59dd69109b2d32e236b87fb45ec314594f3106d04b1dc30a6803f9fdff00869bfda1f3ff00253bc6bff8526a5ffc915fd2affc13dfc1be12f8affb30e85e33f8a7a358f8cfc433ea5ad5bcdabf886dd355d46586d7509e386392eaec4b33ac4802a0672157818afc66ff00874dfed8ff00f42be9dff83cb4afd4ff00d9cff687f855fb0b7c25d2bf67dfda5f549bc37e39d36e350d4eeac2d2caef56852db55bc9ee2d985cd9432c2dbd0f4c86041c81401f767c4df819f052dbe1bf8aee6dbc01e188a68744d45e391347b456475b7908652220410790457f13d7aaab3285000f2613c7a98d49fccd7f573e3dff00829bfec6fadf81bc45a2e9fe30be7babfd26fad6053a0ea8a1a59a0744059ad40196206490077afe512ee449250c872047129faaa2a9fd45007ef3ff00c1127fe43df137fec11a1ffe95ea35fd0657f3e7ff000449ff0090f7c4dffb04687ffa57a8d7f419401fc647edf1ff00276ff14bfec67bbffd15056cff00c13bb42d13c47fb5c7c3bd27c41a7daea76371aa5c24d6d790a5c43228d36f9c078e40cac37283823a807b5637edf1ff00276ff14bfec67bbffd150575ff00f04d5ff93c8f86bff616b9ff00d35ea1401fd578f80bf03b03fe2de785bff04d67ff00c6a97fe142fc0eff00a279e16ffc1359ff00f1aaf571d057cc7f1cbf6c1f809fb396bba77873e2debb71a4dfea968d7d6b1c3a6de5f07815fcb2c5ada191570dc61883401e8bff000a17e077fd13cf0b7fe09acfff008d51ff000a17e077fd13cf0b7fe09acfff008d57c99ff0f4afd8b3fe872bff00fc27f55ffe45a3fe1e95fb167fd0e57fff0084feabff00c8b401f59ffc285f81dff44f3c2dff00826b3ffe3547fc285f81dff44f3c2dff00826b3ffe355f267fc3d2bf62cffa1cafff00f09fd57ff9168ff87a57ec59ff004395ff00fe13faafff0022d007d67ff0a17e077fd13cf0b7fe09acff00f8d57967c71f81ff0005ecbe0bf8f6f6cbc05e1982e2dfc33abcb14b1e91689247225a4acacac220432900823906bd87e127c5bf02fc70f02d8fc48f86f7d26a3a06a325c456f712dbcd6aecf6b33c128314e8922ed91187cca338c8e2a87c7bff00921bf10ffec56d67ff0048e5a00fe18ee005900031f2467f3515fbd5ff00043bff0090b7c61ffaf5f0cffe85a8d7e0b5cffac1ff005ce3ff00d0057ef4ff00c10eff00e42df187febd7c33ff00a16a3401fd05d14514005145140051451401ffd4fdfca28a2800a28a2800a28a2803f9f4ff0082e27fc85be0f7fd7af89bff0042d3abf05adbfd61ff00ae727fe806bf7a7fe0b89ff216f83dff005ebe26ff00d0b4eafc16b6ff00587feb9c9ffa01a00fee6fe027fc90df879ff62b68dffa47157c9bff00054aff00932cf197fd7fe81ffa75b4afacbe027fc90df879ff0062b68dff00a47157c9bff054bff932bf197fd7fe81ff00a75b4a00fe44dddf7b7cc7a9ef4df31ffbc7f3a1fefb7d4d32801fe63ff78fe74798ff00de3f9d328a00fdbcff008228127e2ef8e8939ff8a5a0ff00d2e35fd1f9e86bf9bfff008227ff00c95df1d7fd8af07fe971afe900f43401fc7c7fc14abfe4f23e257fd85adbff004d7a7d721fb03ffc9dbfc2dffb19ed3ff454f5d7ff00c14abfe4f23e257fd85adbff004d7a7d721fb03ffc9dbfc2dffb19ed3ff454f401fd9bd1457e55ff00c1467f6cef8b5fb296b1e0bb3f86b06892c1af58ea77578756b296ed835a4d6b14623f2ae6df6822762d9dd9c0e9401faa9457f2f51ffc163bf6a598b2c763e0edc2391c6747ba00ec52d8cff689eb8afe973c0fabddf883c17a06bfa86c175a9e976779388c6d4f327851df682490bb98e064e05007c35ff0547ff933df117fd863c3dffa73b7afe448f5afee6fe3a7c12f07fed09f0eaf3e1878ee4bd8b47beb8b4b995b4f985bdc6fb399678f6c855f68de833819c7420f35f0b7fc3a17f64eff009ede2bff00c1bfff006aa00e93fe0949ff002687a77fd8c3af7fe95b57e92d78c7c06f815e0bfd9d7e1f43f0d3c0325f49a44177757a87519c5c4fe65dbf9926640a991bba6467d49af67a002bf1bbfe0b3dff00244bc0ff00f6335d7fe9aaf2bf646bf1bbfe0b3dff00244bc0ff00f6335d7fe9aaf2803f9a5d1ffe42d65ff5f317fe862bfb43fd87ff00e4d13e11ff00d8a7a6ff00e8a15fc58dbcef6b7115cc78df13abae791953919afd32f85fff000552fda1fe147c3cf0efc35f0d597859b4af0d69f069b686ef4bb99a730c0bb54c922dfc6acd8ea42283e82803fac4a2bf977ff87c9fed45ff003e3e0eff00c135d7ff002ca8ff0087c9fed45ff3e3e0effc135d7ff2ca803dbbfe0b6848f197c3920e3fe243aaff00e96d9d7e10798ffde3f9d7f47ff00bc1da37fc1527c2179f10ff00696f32df53f056a0da2e98be1677d2a06b4bdb6b5bd733248f74cf2798c0021c2e147cb9e6bdc3fe1d0bfb270c7ef7c57d7fe82fff00da6803f950f31ffbc7f3afd6cff823ab16fda81b7127fe298d77affd75d36bf347e29f8734ef097c41f10f87349f33ec9a6eada959c3e6b6f7f2ad6f2781371c0c9d918c9ee726bf4b7fe08e9ff2740fff0062c6bbff00a374da00fea3abf957ff0082bc92bfb59ea3b4e3fe245a174ff72e6bfaa8af86fe3c7fc13ebe047ed17f10a7f897f10a6d786ad71696d64cba7df8b680456818261046c777ce72493ed8a00fe3a77bff0078fe74dafeacff00e1d0bfb277fcf6f15ffe0dff00fb551ff0e85fd93bfe7b78afff0006ff00fdaa803e3cff008224ff00c87be26ffd82343ffd2bd46bfa0caf937f66efd8d3e117ecb3a86bba97c329758793c436f6b6f76ba9de0bb50966f2bc7e5feed0a9cccd9e483e80e73f595007f191fb7c7fc9dbfc52ff00b19eefff0045415d7ffc1357fe4f23e1affd85ae7ff4d7a85721fb7c7fc9dbfc52ff00b19eefff0045415d7ffc1357fe4f23e1affd85ae7ff4d7a85007f60e3a0afe703fe0b5e48f8bbe0520e3fe2969ff00f4b857f47e3a0afe6fff00e0b61ff2577c0bff0062bcff00fa5c2803f11fcc7fef1fce8f31ff00bc7f3a651400ff0031ff00bc7f3a723bef5f98f51dea2a7a7df5fa8a00febbbfe096bff2659e0dff00affd7fff004eb775f597c7bff921bf10ff00ec56d67ff48e5af937fe0969ff002657e0dffaff00d7ff00f4eb775f597c7bff00921bf10ffec56d67ff0048e5a00fe192e7fd60ff00ae71ff00e802bf7a7fe0877ff216f8c3ff005ebe19ff00d0b51afc16b9ff00583feb9c7ffa00afde9ff821dffc85be30ff00d7af867ff42d46803fa0ba28a2800a28a2800a28a2803fffd5fdfca28a2800a28a2800a28a2803f9f4ff0082e27fc85be0f7fd7af89bff0042d3abf05adbfd61ff00ae727fe806bf7a7fe0b89ff216f83dff005ebe26ff00d0b4eafc16b6ff00587feb9c9ffa01a00fee6fe027fc90df879ff62b68dffa47157c9bff00054bff00932bf197fd7fe81ffa75b4afacbe027fc90df879ff0062b68dff00a47157c9bff054bff932bf197fd7fe81ff00a75b4a00fe445fefb7d4d329eff7dbea69940051451401fb77ff00044fff0092bbe3affb15e0ff00d2e35fd201e86bf9bfff008227ff00c95df1d7fd8af07fe971afe900f43401fc7c7fc14abfe4f23e257fd85adbff004d7a7d721fb03ffc9dbfc2dffb19ed3ff454f5d7ff00c14abfe4f23e257fd85adbff004d7a7d721fb03ffc9dbfc2dffb19ed3ff454f401fd9bd7e04ffc167f41d6f5df11fc328747d3eeef58693ae2b7d96da5b80a4dd5811bbca47db90a719f4afdf6a69556ea01a00fe11edfe1c78eadd9e59340d57688661ff20ebbead1b01d6103a9ef5fdbf7c2d0cbf0cfc228e0ab2e85a68208c1045b47c107a5773e5a7a0fc853e800a28a280336e759d26ce5f22f2f2de09300ec925446c1e870c41a80788741660aba8da1248000b88f249e83ef57f31dff0005859043fb5224ab1c6cff00f08b686a19e3572019751240dc0e33819fa0afcf3f819706e7e2ef839658e1f97c43a23295891483fda36c382003d091f4a00fee5abf1bbfe0b3dff244bc0fff006335d7fe9aaf2bf64057e37ffc167bfe489781ff00ec66baff00d355e5007f32945145005ed3b4dbed5af23b0d3a096e6e263b638a18da576382701103313804e002715d77fc2b2f1eff00d00355ff00c175dfff0019afabff00e09bff00f2781f0d7fec36dffa6ebfafec3446981f28e9e82803f1d3fe08faa7c33f06fc6f67e220da54ede25b7291df2b5a3baae996a85952608c5772919c60e38afd75ff00848b403c7f69d9ff00e0447ffc557f3fff00f05b02b178d7e1bc8123629a16ac577a2b004de5a0c80475c122bf0e6c2e9ae2592296280a9b7b93c431820ac2e41042e4104645007b87c6af01f8c755f8ade2ebcb1d13529a17d7f592922585cba3abea172eacae9132b2956041048e6bef9ff824ae83ac7853f69b6b8f12595d69907fc233ad0f3af6de5b588b3cb61b543cc91a963b18e01ce057f4a3e0f443e13d17e51ff20eb4edff004c52bf3f7fe0ab91c67f642d49591581f10e84082a0820dd2e4104720d007e877fc247a07fd04acfff000223ff00e2ab4ad6eed6f62f3ed258e78c9203c6e1d723af2b91c57f035fda137fcf383fefc47ffc4d7f5bbff04b6444fd8efc3891aaa28d5fc4202a80000353b8c0007000a00fd10a28a2800a28a2803f8c8fdbe3fe4edfe297fd8cf77ffa2a0aebff00e09abff2791f0d7fec2d73ff00a6bd42b90fdbe3fe4edfe297fd8cf77ffa2a0aebff00e09abff2791f0d7fec2d73ff00a6bd42803fb071d057f37fff0005b0ff0092bbe05ffb15e7ff00d2e15fd200e82bf9bfff0082d87fc95df02ffd8af3ff00e970a00fc44a28a2800a7a7df5fa8a653d3efafd45007f5ddff04b4ff932bf06ff00d7febfff00a75bbafacbe3dffc90df887ff62b6b3ffa472d7c9bff0004b4ff00932bf06ffd7febff00fa75bbafacbe3dff00c90df887ff0062b6b3ff00a472d007f0c973feb07fd738ff00f4015fbd3ff043bff90b7c61ff00af5f0cff00e85a8d7e0b5cff00ac1ff5ce3ffd0057ef4ffc10effe42df187febd7c33ffa16a3401fd05d14514005145140051451401fffd6fdfca28a2800a28a2800a28a2803f9f4ff0082e27fc85be0f7fd7af89bff0042d3abf05adbfd61ff00ae727fe806bf7a7fe0b89ff216f83dff005ebe26ff00d0b4eafc16b6ff00587feb9c9ffa01a00fee6fe027fc90df879ff62b68dffa471579dfed83f0375dfda37e026bbf093c39a8da6957fab5c69b347757caed020b1bc86e58308fe63b9622063b9eb5e87f0108ff00851bf0f3fec55d1bff0048e2af5ac8a00fe6fcff00c1143e2e9249f1d785b9ff00a617dfe347fc393fe2effd0f5e16ff00bf17dfe35fd20645191401fcdfff00c393fe2eff00d0f5e16ffbf17dfe349ff0e4ff008bbff43d785bfefc5f7f8d7f483914645007e58fec15fb05f8f3f647f1e788bc53e29f1168facdaeb3a3c7a745169d1dc2491c897026dcde7020a9191c1f4e2bf534f434b914d2460d007f1f3ff052aff93c8f895ff616b6ff00d35e9f5c87ec0fff00276ff0b7fec67b4ffd153d75dff052aff93c8f895ff616b6ff00d35e9f5c8fec0fff00276ff0b7fec67b4ffd153d007f66f5f16fed59fb6e7807f64ad47c3da778d340d67596f11db5e5cc0fa59b50b1259490c6fe61b99e1e4b4ebb76e7bf4afb4abf9f3ff82db7fc87be197fd8235cff00d2bd3a803e81ff0087d07c04ff00a12bc59ff7de95ff00c9f4bff0fa0f809ff42578b3fefbd2bff93abf98dab42caf5806582520f20843cfe9401fd71fecdfff00051cf85dfb4cfc4cb7f85fe11f0cf8834cbeb9b2bbbd5b9d40d91b7096610ba9fb3dcccfb8ef5c7cb8f7afd0fafe56ff00e09150cb07ed67a68991a32741d771b815fe0b6f5afea8f23d6803f21bf6ddff0082757c44fdaa3e327fc2c5f0e789b43d234f1a369fa77d9f508ee5e732d9bdcb17cc3850a44f81c93c76eff2c785bfe090df163e1c788b4df1ddcf8d3c35716fe1fbcb5d5a782186f165962d3a78ee9a342f950ce22da0918c9e7d6bfa21eb5cdf8c7fe453d6bfec1d77ff00a25e803f268ffc1677e032852de08f172ef557019b4b076b80cbc1bec8c820e0f35c3fc46f1b695ff0565f0d45f0dfe0f4371e0dbbf03de47aededc789d62920b9b7bd82eac56384584f3b6f0c589de54003be715fcebeadff001f31ff00d7b5affe884afdceff0082257fc8e3f11bfec03a57fe96de5007327fe08a1f1740ff0091ebc2dff7e2fbfc6bf2afe3f7c1bd57e02fc55f107c2dd66fadb51bbf0fde0b396e6d15d61918c10cf94127cc06d980e79c83ed5fdca1e86bf8efff00828fff00c9e07c4aff00b0e2ff00e9bac2800ff82707fc9e07c35ffb0e37fe9bafebfb101d057f1dff00f04e0ff93c0f86bff61c6ffd375fd7f6203a0a00fe797fe0b6bff238fc39ff00b00eabff00a5b675f855633a5bcecefd1a29a3fa19236407e809e6bf757fe0b6bff238fc39ff00b00eabff00a5b675f83d401fd2ee85ff000591f80fa5e8ba7e9b27833c58cf6b6b040cc1f4bc168e3552466f81c64771f957cc3fb6a7fc14a3e137ed1df02eefe19785bc35e20d33509f54d36f96e2fdac0c012ce612ba9f22ea5932c0617e5c67a902bf12858de91916f2907fd86ff0a6496b7312ef9629117a659481fa8a008475afebb7fe0971ff00267be1dffb0c7887ff004e7715fc890eb5fd76ff00c12e3fe4cf7c3bff00618f10ff00e9cee2803f43a8a2932280168a4c834b401fc647edf1ff00276ff14bfec67bbffd150575ff00f04d5ff93c8f86bff616b9ff00d35ea15c87edf1ff00276ff14bfec67bbffd150575dff04d5ff93c8f86bff616b9ff00d35ea1401fd840e82bf2cbf6f5fd82fc79fb5c78f3c3be29f0b788b47d1ad746d1e4d3a58b518ee1e49247b8336e5f2400140c0e4faf15fa9808c0a7645007f37dff000e4ff8bbff0043d785bfefc5f7f8d2ff00c393fe2eff00d0f5e16ffbf17dfe35fd20645191401fcdff00fc393fe2effd0f5e16ff00bf17dfe340ff0082287c5d0411e3af0b71ff004c2fbfc6bfa40c8a322803e62fd8fbe06ebbfb397c04d0be12788f51b4d56ff49b8d4a692eac55d60717d7935ca85127cc36aca01cf71d6bd13e3dff00c90df887ff0062b6b3ff00a472d7ace45792fc7b23fe146fc43ffb15759ffd2396803f865b9ff583feb9c7ff00a00afde9ff00821dff00c85be30ffd7af867ff0042d46bf05ae7fd60ff00ae71ff00e802bf7a7fe0877ff216f8c3ff005ebe19ff00d0b51a00fe82e8a28a0028a28a0028a28a00ffd7fdfca28a2800a28a2800a28a2803f9f4ff0082e27fc85be0f7fd7af89bff0042d3abf04158a1cafa11f81183fa57f4a3ff00056df805f183e36ea9f0c24f85be14d4fc4b1e8f6daf2df369d12cbe435d358f941f73a63788df18feed7e38ff00c303fed6dff44b7c4fff008091ff00f1fa00d9d0bfe0a23fb5c787344d3fc3da4fc44d52dec74bb586ceda14b7d34ac70dba08e3405ac59b0aaa00cb13ea4d6aff00c3cabf6c8ffa295ab7fe03697ffc815c8ffc303fed6dff0044b7c4ff00f8091fff001fa3fe181ff6b6ff00a25be27ffc048fff008fd0075dff000f2afdb23fe8a56adff80da5ff00f2051ff0f2afdb23fe8a56adff0080da5fff00205723ff000c0ffb5b7fd12df13ffe0247ff00c7e8ff008607fdadbfe896f89fff000123ff00e3f401d77fc3cabf6c8ffa295ab7fe03697ffc8147fc3cabf6c8ff00a295ab7fe03697ff00c815c8ff00c303fed6dff44b7c4fff008091ff00f1fa3fe181ff006b6ffa25be27ff00c048ff00f8fd0075dff0f2afdb23fe8a56adff0080da5fff0020527fc3cabf6c8ffa295ab7fe03697ffc815c97fc303fed6dff0044b7c4ff00f8091fff001fa3fe181ff6b6ff00a25be27ffc048fff008fd007cebf123e2478c3e2cf8c351f1df8ef519755d6b559566bbbb99634795d23484332c291c6088e345f9514614719c93f457ec0ff00f276ff000b7fec67b4ff00d153d1ff000c0ffb5b7fd12df13ffe0247ff00c7ebe98fd8f3f63cfda5be1f7ed2df0e7c57e2bf875e20d3748d37c416d737979736c890c10a24aa5dcacac4005864e2803fa9fafe7cff00e0b6dff21ef865ff00608d73ff004af4eafe832bf9f3ff0082db7fc87be197fd8235cffd2bd3a803f066cd16499838c8114ac3eab1b107f022bfaf9f879fb057ec81ab7807c35aaea3f0bf459aeaf347b0b89e5612e5e596046763fbcea58926bf907b26559cee38cc5328f72d1b003f12715fd90fc39fdb1bf655d37e1ef8634ebff8b3e1082e6d746d3e19a29357b7578e48edd1595817c82a41047ad007ce1fb5bfc16f85bfb227c0ed63e357ecdbe1db3f0178dec6f34bd3edf5ad3503dc476ba8dec305cc616e3ce888923623e68db0704722bf157fe1e53fb647fd14ad5bff0001b4bffe40afdbcfdb5be2a7c36fda5ff67bd6fe15fecfde27d27e2178c6eafb48bd8343f0f5ec37b7f2dbd8df4335c48b1abfdd8e352cc4900773c8afc15ff8607fdadbfe896f89ff00f0123ffe3f401fd28ffc13bbe28f8ffe30fecd965e35f895accdaf6b72eb5abdabde5c2431bb436d7063894ac11c51fcaa3b28cd7d85e31ff914f5affb07ddff00e897af8bff00e09bdf0fbc6ff0cbf663b1f0a7c41d12f3c3fac47adeb170f657e82399629ee0bc6c402c30ca72082457da1e31ff00914f5aff00b07ddffe897a00fe0eb56ff8f98ffebdad7ff44257ee77fc112bfe471f88dff601d2bff4b6f2bf0c756ff8f98ffebdad7ff44257ec37fc123be317c2af84be27f1ddefc4ef16691e1682fb46d360b57d5aee3b459a58aeee9dd10c840665575240e8181a00fe988f435fc77ffc147ffe4f03e257fd8717ff004dd615fd399fdb4ff64ac1ff008bbde0dffc1c5b7ff175fcb57edebe2ef0b78e7f6a3f1f78a3c1bab59eb7a46a1ab89ad2fac6659ede78fec3671ee8e4525586f465c8e32a476a00e8bfe09c1ff2781f0d7fec38dffa6ebfafec407415fc66fec15e2ef0b781bf6a3f00f8a3c65ab59e89a469fab99aeefafa6582de08fec3791ee924621546f755c9e32c077afea547eda7fb25607fc5def06ffe0e2dbff8ba00fc82ff0082daff00c8e3f0e7fec03aaffe96d9d7e16e9d124b70cb20c8582e1c76f99227653f8100d7ef97fc1483c33ac7ed87aef8375ffd98adcfc4dd2b43d3751d3b53bdf0cbc57f059ddcd716b3c714ae24550ed1a16db9ce3071820d7e665afec21fb5b5a3c92ffc2abf13b930cd181f648c64c91b20ff0096c7b9f43401fd24f863f603fd8eef7c39a55ddcfc2dd1249a6b1b69247226cb3bc4a589fde7524e6be31ff828bfec8dfb37fc25fd9a2ffc61f0e7c07a6685acc7ad68f6cb796a24122c3717212551b9c8c3a9c1e3a57e81e8bfb5e7ecbda069165a16b7f153c2763a8e9d6d0da5ddadc6ab04734171022a491488cc0aba3a95652320820f35f29fedc9f12be1ff00ed41f002ff00e177ecefe23d2fe22f8b9f54d2f515d17c3b790df5e9b3b3b9579e6f2d1f848c119624004819c91401fcb00eb5fd76ff00c12e3fe4cf7c3bff00618f10ff00e9cee2bf9d6ff8607fdadbfe896f89ff00f0123ffe3f5fbd5fb14fc54f86dfb347ecf7a27c2bfda07c4fa4fc3df18dadf6af7b3e87e21bd86cafe2b7bebe9a6b791a367fbb246c1948241ec783401fa39f10f50bdd27c03e25d574d99adeeecb47bfb8825500b472c503b230c8232ac01190457f2572ff00c1493f6ca80a46df13355663144cc45ae96012e8ac703ec070327d6bfa35f88dfb637ecaba97c3df13e9d61f167c213dcdd68da8430c51eaf6ecf24925bbaaaa80f92589000f5afe37af595a71b4e71142a7d8ac6a08fc08c5007f4b7ff04aff00da53e35fc7ed77c7d17c59f155df88a0d2b4dd227b18eea1b58fc892e2e2f2394a9b6820cee5853ef6718e3ad7ecb57f3e7ff0449ff90f7c4dff00b04687ff00a57a8d7f419401fc647edf1ff276ff0014bfec67bbff00d15057cebf0dfe2478c3e1378c34ef1df81351974ad6b4a95a6b4bb85637789de3784b2acc9246498e475f9918618f19c11fa45fb61fec79fb4b7c41fda5be2378afc29f0ebc41a9691a97882e6e6cef2dad91e19e1748943a16954904a9c1c57ccfff000c0ffb5b7fd12df13ffe0247ff00c7e803adff0087957ed91ff452b56ffc06d2ff00f90297fe1e55fb647fd14ad5bff01b4bff00e40ae47fe181ff006b6ffa25be27ff00c048ff00f8fd1ff0c0ff00b5b7fd12df13ff00e0247ffc7e803aeff87957ed91ff00452b56ff00c06d2fff009028ff0087957ed91ff452b56ffc06d2ff00f902b91ff8607fdadbfe896f89ff00f0123ffe3f47fc303fed6dff0044b7c4ff00f8091fff001fa00ebbfe1e55fb647fd14ad5bff01b4bff00e40a3fe1e55fb647fd14ad5bff0001b4bffe40ae47fe181ff6b6ff00a25be27ffc048fff008fd1ff000c0ffb5b7fd12df13ffe0247ff00c7e803aeff0087957ed91ff452b56ffc06d2ff00f902b2b5dff8288fed71e23d1350f0f6adf11354b8b1d52d66b3b985edf4d0b2437086391095b156c32b1070c0fa1158dff0c0ff00b5b7fd12df13ff00e0247ffc7e8ff8607fdadbfe896f89ff00f0123ffe3f401f1e3317396f403f00303f4afdefff00821dff00c85be30ffd7af867ff0042d46bf34ffe181ff6b6ff00a25be27ffc048fff008fd7ec77fc124be017c60f825aa7c4f93e29785353f0d47ac5b682b62da8c4b179ed6ad7de684daef9d8244ce7fbd401fb53451450014514500145145007ffd0fdfca28a2800a28a2800a28a2800a28a2800a2bf0abe25ff00c161b5ef87df117c55e051f0e34bb94f0e6b9a9e8e93c9ad5cc6f32e9f7525b891916c64552fe5eeda19b19c64d713ff000fb6d7bfe899691ff83dbbff00e575007f417457f3e9ff000fb6d7bfe899691ff83dbbff00e5757eacfec69fb48ea3fb537c239be276a5a15bf87a44d62f34c5b4b7ba7bc42b6823fde798f14272c5cf1b78007738001f5951451400514507819a0028afc69fda53fe0aa1aefc00f8d7e2af84d0f8074ed520f0eddc56b1df4fabdc5bc93f996b05c9631476532ae3cfdbf7ce719e2b1be037fc15975ff8d1f183c23f0bdbe1ee99a7c5e25d561d3a4bc8758b89dedd65576de237b28836021e370e6803f6bebf9f3ff82db7fc87be197fd8235cff00d2bd3abfa0caf85ff6befd877c39fb5dea7e1ad4b5ff0014df787bfe11bb5bdb548eced60b813adec9048c5fcece369817181dfad007f1d757d755d4d542addce00180048d8007e35fd1d7fc394be198ff009a91ad751ff30bb1ff00e26bf9f8f8abe0fb7f00fc43f11783ad2e1eea1d1b57d4b4e8e67508d22d8de4d6cae557805c441881c027038a00fd19ff0082465d5cdd7ed69a6b5ccaf295d075e00bb16c7c96deb5fd50d7f153fb23fed2573fb2e7c56b7f89969a2c1aec9058df590b5b8b992d2322f563058c91c533657cbe06c39cf518afd4bff87db6bdff0044cb48ff00c1eddfff002ba803fa0bae6fc63ff229eb5ff60fbbff00d12f5e09fb21fed037ff00b4cfc1bb7f8a7a968b068134fa95fd81b2b7b97bb8c0b297cade2578e263bf19c6c18afa3b57d3c6ada5de696ce621776f2c05c0c95f350a671c6719cd007f04fab7fc7cc7ff005ed6bffa212a9c175736a49b695e22c304a315c8fc2bfa433ff0454f86ee13cdf895adbb2a2264e996478450a3a827a0ee49f7af863f6eeff827df847f650f00787fc5da078b2ff5d9b58d5a6d3de1bbb3b7b758d23b39ae770308049262db8391839eb401f95bfdadaaff00cfe4ff00f7f1bfc6a94b2c933996676776e4b31c93f526a3a280248a5921712c2ec8ebc8653823e8455dfed6d57fe7f27ffbf8dfe35eeffb2cfc1cd37e3cfc6ff0a7c2ed5b509b4bb5d7efcd9c97504692c91016d713ee5493e4273005c1e3049ea057ee00ff0082297c33233ff0b235affc15d8ff00f13401d37fc11964925f82be3a92562eede26b52598e493fd9567d49afd8d35f865e2bf1ec7ff048fb1b3f873e14b31f112d7c74f71afcd79ab5c7f653d9b592dad888512d6de7575652a724291839ce45705ff0fb6d7bfe899691ff0083dbbffe575007e3c7c74bfbeb7f8b9e308edee258d3fe121d6ced472a32752baec0d7e85ffc11eeeae6ebf6a22d732bca57c2fae805d8b6079ba77ad7e5cf8f7c527c6be2fd5fc52d6eb6a755bfbdbe30ab17119bcb896e0a86201214c9b4120120670338afd3ff00f823a7fc9d03ff00d8b1aeff00e8dd36803fa8eafe57bfe0ae775736bfb5a6a4d6d2bc45b41d04128c573f25cfa57f5435f9a9fb507fc136fc21fb4f7c54b9f8a3aef8d352d167b8b1b3b1fb1dad95b4f105b30e15b74d96c9f30e7a7e3401fc9eb6aba9b2956bb9c82304191b041fc6a857f44de30ff8235fc37f0e784b5bf1043f113589a4d334ebbbc48db4db250ed6f13481490b90095c64735fcf05c4421902039ca46fff007da86c7e19a00fde9ff8224ffc87be26ff00d82343ff00d2bd46bfa0cafe7cff00e0893ff21ef89bff00608d0fff004af51afe832800a2bf143e3cff00c15975ff0082ff00183c5df0bd7e1ee99a845e1ad566d3a3bc9b58b881ee162546de634b2942e438e371e6bc8ffe1f6daf7fd132d23ff07b77ff00caea00fe82e8afe7d3fe1f6daf7fd132d23ff07b77ff00caeafd39fd88ff006acd47f6b5f00ebfe34d47c3b6de1c6d1f591a5a416b7925e24aa6d60b9126f9218083fbedbb76f6eb401f69514d63b549f415f819af7fc1683c47a0eb7a868f2fc33d249b2bbb9b5dcdae5d02df6795e2dd81a7b01bb667193d6803f7d68afcabfd8c7fe0a33ac7ed5df16a6f86b79e0bb1d0608b45bdd585e5aea735e396b496da2f2cc725ac0006fb46770638dbd39afd54a0028a28a0028a28a0028a28a0028a28a0028a28a00fffd1fdfca28a2800a28a2800a28a2800a0d141a00fe1e3f69cff009388f89fff0063af893ff4e5715e155eebfb4e7fc9c47c4fff00b1d7c49ffa72b8af0aa002bfab3ff82427fc9a74dff635eaff00fb46bf94cafeacff00e0909ff269d37fd8d7abff00ed1a00fd49a4cfd7f2ac8f10b32685a8ba31565b49c865382088db9047435fc4a4ff00b467c73b63144be3df14bfee6262cde20d5324b20249c5d81c93d801401fdc167ebf95213c1ebf957f0eff00f0d27f1d3fe87bf147fe143aafff0025d4f6bfb47fc729ae6289fc77e28daeeaa7fe2a1d57a138ff009fba00f7ff00f829242d3fed95f13111e30cbaada92af2221c369761838620e0e0f35c8fec1b09b7fdadfe16798f165bc4d6980b2a39e239bb2927b8fcebfa4bfd887c2be18f1bfeca7f0e3c57e34d22c75fd6f53d27cfbdd4b54b68ef6f6ea532c837cd3ccaf2c8d80065989c003a015f59d9fc38f87ba75dc37fa7786346b5bab771243343a7dbc7246e3a32b2a02a47620e6803b4a4cd2d7e137fc1623e2278ebc07e21f86ede0df10eafa22cfa56b524c9a6ea37560b33c77360a8d20b6962de543b05dd9c64d007eeb93f5ea2bf877fda5011f1d7c780f1ff001547887ff4eb794a3f694f8ea0e478f3c51c7fd4c3aaff00f25d78cea3a8deead7b3ea3a8cf25cdcdcc8f2cb2caed248f248c599999896666624b3124b124924926802951451401fd6bffc1293fe4d0f4eff00b1875eff00d2b6afd25afcdaff0082527fc9a1e9dff630ebdffa56d5fa07e2e778fc2dac491b3232d85d156524302227208239045007439fafe55f8ddff059e23fe149f817732a6ef13dca82ec1172da5de0032d81c9381ef5f82377fb457c72b478e14f1ef8a587d9edd8b3788354c96789189e2ec0e493d062bf5fff00e0929e22d6fe3178b7c7b6ff00152faefc5f6f65a369b716906bf733eaf15b4d25d5d46f242b7b24fe5b3a22ab15c640e6803f047ec13ff7a1ff00bff17ff1555648da26d8f8cfa82181fa11907f0afeef0fc2bf86183ff148683ff82cb6ff00e375fc8eff00c143f4cd3748fdadbe2358695690595b43ad2ac70dbc6b1468bfd9f6270aa80003249e07527d6802f7fc1383fe4f03e1affd871bff004dd7f5fd880e82bf8eff00f82707fc9e07c35ffb0e37fe9bafebfb101d05007f3cff00f05b101fc6bf0de20e8acfa16aa143baa648bcb438058819c027f0afc29fb04ffde87feffc5ffc557f799ad783bc25e249a2b8f11e89a76ab2c0a5227bdb48ae19158e4aa991588048c903bd629f857f0c4608f08e83ff0082cb6ffe37401fc20b2953b4f515fadbff000474ff0093a07ffb1635dffd1ba6d7e757c73b7b7b5f8b5e2d82da248624d7b595548d42aaaaea372a000300000000760315fa2bff000474ff0093a07ffb1635dffd1ba6d007f51d4514500707f153fe49878bff00ec03a9ff00e93495fc265fff00af5ffae307fe8a5afeecfe2a7fc930f17ffd80753ffd2692bf84cbff00f5ebff005c60ff00d14b401fbc7ff0449ff90f7c4dff00b04687ff00a57a8d7f4195fcf9ff00c1127fe43df137fec11a1ffe95ea35fd065007f191fb7c7fc9dbfc52ff00b19eefff0045415f1d57d8bfb7c7fc9dbfc52ffb19eeff00f45415f1d50015fd397fc117ff00e48278d7fec6d4ff00d355857f31b5fd397fc117ff00e48278d7fec6d4ff00d35585007ec3cbfeadbe87f957f085f137fe47dd7ffec2ba8ffe95cd5fddecbfeadbe87f957f085f137fe47dd7ff00ec2ba8ff00e95cd401fa71ff00046bff0093a3bdff00b13f58ff00d2ad36bfa88afe5dff00e08d7ff27477bff627eb1ffa55a757f51140099a33f5fcabf97cff00829cfc5ff8a1e08fdae3c51a4f85bc5bafe9760b61a33a5a596b17d696e8d25a02ecb1413c68a58804e07279ea493f9f3ff0d27f1d3fe87bf147fe143aafff0025d007f7139fafe54b9afe1d7fe1a4fe3a7fd0f7e28ffc28755ffe4bafdb7ff8236fc49f1dfc41d57e2baf8d3c41aaeb6b636de1e36eba96a3777e213335fef31fda669766ed8bbb6e33819e82803f74e8a28a0028a28a0028a28a00ffd2fdfca28a2800a28a2800a28a2800a28a2803f9a0f8c9ff0004b1fda8fc77f16bc6de32d22c3447d3f5df126b1aa5a33eb29139b7bebc9a78b72181b6b6c719193835e6dff0e85fdacffe81da17fe0f53ff0091abfaa8a2803f956ff8742fed67ff0040ed0bff0007a9ff00c8d5fa2bfb337c5ef007fc13cfe1c37c01fda5f50934af17cda85cf8892df48b3bbd62dffb3f516d90399eda02818bc12295201f9738c115fb295fcbbffc164ffe4e8ecbfec4fd1fff004ab51a00fd71bcff00829a7ec8fad5acba2d87887566bad414d9dbab683a8a2b4d71fba8d4b340146e76032481cd7e38bffc122ff6b5b811bc9a7683b8471a12baea00762819c7d98e338f535f9c9f0cbfe47dd03fec2ba77fe95c35fddec7f717e83f95007f2b5ff0e85fdacffe81da17fe0f53ff0091aa487fe0915fb59c12a4c34dd0498d8363fb753f84e7fe7dabfaa4a43d0d007e517c16fdadfe077ec89f0b7c3bfb36fc6ad5ef2c7c6fe02b34d375ab7d3f4bbdd42d63b871f68511dcc10b4720314c8dc1c8dd820115ef9e03ff008288fecbbf123c67a2f807c29aeea771acebf789636314da25fdba493c8090a64961545e149c93d057f3b3ff00052aff0093c8f895ff00616b6ffd35e9f5c87ec0ff00f276ff000b7fec67b4ff00d153d007f66f5fcf9ffc16dbfe43df0cbfec11ae7fe95e9d5fd0657f3e7ff05b6ff90f7c32ff00b046b9ff00a57a75007e07d145140051451401fd6bff00c1293fe4d0f4effb1875effd2b6afd0bf11d9cfa8e81a969f6a034d7367710c609c02f246caa33db93d6bf3d3fe0949ff2687a77fd8c3af7fe95b57e92d007f2b937fc1237f6b7ba31c92e9ba08758a28ce35d40331a2a671f6738ce338c9fad7e99ff00c136bf63af8cff00b2ef8a3c65a8fc50b4d3aded359d2ec2d2cdacb505bd6692dee2795f7811c7b462518e0f4afd70a28010f435fcf67ed75ff04defda47e367ed0de36f88de11b0d1e4d135bd4d6eaca4b8d592de668fec96d09df19864dbf34271f374ed5fd0a51401fcf5fec8bff04defda47e09fed0de09f88de2eb0d1e3d1344d4daeaf64b7d592e2658fec97308d918863ddf34c33f374ed5fd098e82968a00f9a3e3b7ed6ff0004ff00670d5349d1fe2aea77b6175addb4d7566b69a6dd5f878addd6390936f1becc33a8f9b19c8af076ff0082a47ec76aacede23d602a02cc4f87b53002a8c924fd9f8000c935f9e7ff0005b4ff0091c7e1cffd80755ffd2db3afc31d2bfe3e64ff00af6baffd10f401fadbe3bff825bfed47f107c61ad78d348d3f443a7eb7a95f6a566d26b291486dafee65ba88ba181b6bec94061b8e0f7afaff00fe09f3fb07fc7bfd9b3e3a1f1d7c45b2d2e1d18e85a9d8f9967a9ade4bf68bb7b4641b0451e1710373cf26bf667c1dff00229e8bff0060fb4ffd1295d250014514500707f153fe49878bff00ec03a9ff00e93495fc265fff00af5ffae307fe8a5afeecfe2a7fc930f17ffd80753ffd2692bf84cbff00f5ebff005c60ff00d14b401fbc7ff0449ff90f7c4dff00b04687ff00a57a8d7f4195fcf9ff00c1127fe43df137fec11a1ffe95ea35fd065007f3a5fb507fc1343f698f8bdf1f7c75f113c33a7e8cfa3ebdaddc5f58b4daba412986448d4178cc2fb4fc99c67bd7c83f15bfe099dfb47fc1ff00879ae7c4bf175968f168fe1fb6fb55e3c1aba5c4a23dcabf2c62042c72dd370afebaebe37ff82837fc99a7c55ffb021ffd1d1d007f19d246d148d13fde462a7ea38afe9bff00e08bff00f2413c6bff00636a7fe9aac2bf997beff8feb8ff00aeafff00a11afe9a3fe08bff00f2413c6bff0063627fe9aac2803f621c165207706bf978f157fc1277f6b0d7fc49aa6b11e9ba108ef2faeee23ff89da29d93cf24ab91f676c101b07935fd44d1401fcf5fecbdf00bc67ff04e6f8852fc77fda58d9e93e0fb9d2aebc3b1dc6957126b170750d466b69605305bc01c215b671bb07e62a38cd7e837fc3d1ff63dff00a18b58ff00c27b53ff00e47ae0ff00e0af7ff269d0ff00d8d7a47fed6afe54109debf51401fb93fb477eca7f137f6f6f8a1a87ed23fb3f269fa9781b5f82d6cec2e751bc6d2ae9a6d255ad2e43db4f019102ce8ea37004edcf422be68f157fc129bf6a4f09786757f156aba7e88b65a358dcea170535a4918436b1b4ae557ece371daa703233eb5fb79ff04b4ff932bf06ff00d7febfff00a75bbafacbe3dffc90df887ff62aeb3ffa472d007f0bce850e1bd01fc08c8fd0d7ef7ffc10effe42df187febd7c33ffa16a35f82d73feb07fd738fff004015fbd3ff00043cff0090b7c61ffaf5f0cffe85a8d007f417451450014514500145145007ffd3fdfca28a2800a28a2800a28a2803f2b7fe0a43fb657c54fd94350f005bfc371a518fc4d06b125e7f69583de9dd60d6823d9b6e6df6e44ed9ceecf1d3bfe62ffc3e27f6a370e224f0b9708eca1b4394025549c1235138ce3ae0d7d03ff05c4ff90b7c1eff00af5f137fe85a757e0adb902439fee49ffa01a00feedbe14f88753f16fc30f08f8af5b3136a3ace85a6ea176604314467b9b78e590a21662abb98e14b3607193d6bbfaf06f813e2cf0bc1f047e1f4336b1608e9e17d19595aea2041167164105f822bd57fe131f09ffd0674ff00fc0b87ff008ba00e92be3ef8e7fb0dfc04fda27c6b1f8ffe255a6ab3eaf1d841a687b2d4e7b38fecf6ef2c91829110090d339c9e79afa77fe131f09ffd0674ff00fc0b87ff008ba3fe131f09ff00d0674fff00c0b87ff8ba00f81ecbfe095ffb2569f7b6da85b69fe20135acd15c445b5cba602485c3a1209c1c3283cd7e8d8014003b715ce7fc263e13ff00a0ce9fff008170ff00f1747fc263e13ffa0ce9ff00f8170fff0017401d2515cdff00c263e13ffa0ce9ff00f8170fff001747fc263e13ff00a0ce9fff008170ff00f17401f1efc51ff82777ecd9f183c7dacfc4af1ad96b52eb7af4c97178f6babdc5b42cf1c31c0a5628c855fddc4a38eb8af09f895fb0e7c00fd97fe1f788ff00688f85d61aa278bbe1d69779e22d15b51d52e6f2cc5ed8c2ef1f9d03b01221e430c82413820f35fa71ff00098f84ff00e833a7ff00e05c3ffc5d7ce7fb5e6b5a46bffb2f7c54d1342bdb6d4751bef09eab6f6b6969324f713cd240ca91c71a333bbb1202aa8249e073401f85375ff0584fda86d6e65b665f0bb189d9091a14b83b4e3fe8255f1efed3dfb60fc4dfdaaae743baf88aba609341b7bab6b63a758b590d977243249bc35c5c6e3ba04c1057033c1ce479aea1f033e2e5c5f5c5c47e10f106c925775ce89a90386248ff00975ae13c53e03f17f828dbaf8a748bfd28dd2b3422facae2ccc8a84062a2e228cb004804ae402467191401ccda46b2ca55fa08e571f544661fa8afea8fc07ff04b8fd93b5cf03f87b5bbfd3f5f373a86956375314d72e957cc9a0476c28380324e076afe57ec48133678fdccff00fa2dabfb92f85fe2ef0b45f0d3c251c9ac69eac9a169aac0ddc20822da3c83f3d007e3cfedd1fb00fecebf03ff00674d67e21780acb588758b4d4748b689eef55b8ba8847797b1412e63909527639c1c641e457f3cf5fd6a7fc14efc47e1fd43f642f10dbd8ea7677129d5fc3e4245711bb1035280938562781cfd2bf92b3d6803fad7ff0082527fc9a1e9dff630ebdffa56d5fa4b5f9b5ff04a4ff9343d3bfec61d7bff004adabf496800a28a2800a28a2803c3bf696f881aff00c29f807e3cf893e16101d5fc37a15e6a3662ea332c0668137289103296538e4061f5afe7aaebfe0b09fb50dadd4d6acbe17630c8d1923429704a9233ff00212afde8fdb46c2ff54fd947e2ae9ba5db4d797775e17d42282dede3696596478f0aa8880b3313d00049afe42f50f819f172e2fee6e23f08788364b348eb9d1352070cc48ff975a00fde7fd9bfc33e1fff00829bf84350f887fb4ddaf9fa9f837513a36927c3d2cfa3442cef2d6daf64f3512698bb99180c97c61460039cfd0adff04a3fd90d9190e9de21c3ab29ff0089edd1c861823ae0820f43c578effc1242ceebc01f083c69a6f8e2197c3f753788ede4861d5a37d3e59634d36d633224774b13b26f465dc1704835fac9ff00098f8487275ad387fdbdc3ff00c5d006be9d630699636fa75b6ef26d618e08f71cb6c8d42ae4f7381cd5da623ac8a1d0865600820e4107a1069f40057e21feddff00f0506f8e7fb35fc7bbdf875e061a11d1a1d2f4cbc8fedda6497771e6de2ca5f2eb7700da3cbe06d3d6bf6f2bf98dff0082aafc37f1ef8bff006aad4af7c37e1dd5f51b5fec3d1505c59e9b77750978d2e372f9904322ee5dcb919ef401c76bdff0574fda5bc41a1ea3a0de2786bc8d4ad27b497668922b6c9d1a36c31d45b070dc1c1c7a1afcab9a5699f7b765551f4450a3f415ea971f037e2cdadbcb753f84b5e48a14691d9b46d455555064925ad400001c92401dcd793b295386e0d007d67fb30fed83f137f655b9d72ebe1d2e9864d7aded6dae4ea362d7a365a493491ec0b716fb4ee9df2496c8c703193f5dff00c3e2ff0069ff00ee7863ff000452ff00f2cabf2428a00fd6ff00f87c5fed3ffdcf0c7fe08a5ffe59579bfc5eff00829f7ed01f197e1b6bdf0c3c529a07f64f886dbec977f65d224b79bcbdcac76486fa50872bd7637d2bf3628a007cb234b23caff79d8b1c7a939afb83f66afdbd3e30fecbde11d4bc1df0f9347365aa6a03529cea1a73de49e70822b7f9596eadf6aec8578da79c9cf381f0e5779e19f867e3bf18d949a8786741d5354b6864f25e5b2d3eeeed164da1b6b35bc32aab6d20e0907041c6083401fa67ff000f8bfda7ff00b9e18ffc114bff00cb2a3fe1f17fb4ff00f73c31ff0082297ff9655f9dff00f0a17e2fff00d09fe20ffc126a5ffc8b5e4d3c12db4af04e8d1c91b156560559594e0820f2083c107906803f7b3f671fda0fc57ff0526f1d4bf007f68ab7d3e7f08dbe9973e2355d0ede6d22f3edfa6cd6d1404ce2ea7263c5d392a029dc14e78c57dcbff0ea4fd90ffe81de21ff00c1f5dff8d7e4e7fc11affe4e8ef7fec4fd63ff004ab4eafea22803cb7e0d7c1ef057c07f87f61f0cfe1f45730e87a6cb7334097770d73307bb99ee252d2bfccd992463cf4cd769e29f0e69be30f0ceade13d64486c35ab1b9d3ee844e6390c1751b4526c71cab6d63861c83cd3eefc4be1eb0b86b5bed4ecede64c6e8e5b88e3719191956604645431f8b3c2f34890c3abd83c92305545ba88b331380000d9249e82803f3e57fe0945fb2122aa269be200aa028035dbb0001c00066be8efd9f7f649f83bfb325c6bb75f0aedb51b793c4496b1df1bfbf96f772d9194c5b7cd27660ccf9c75cfb57d3345001451450014514500145145007fffd4fdfca28a2800a28a2800a28a2803f9f4ff0082e27fc85be0f7fd7af89bff0042d3abf03ba722bf7c7fe0b89ff216f83dff005ebe26ff00d0b4eafc0ea00d04d4ee514204b721400336d093c7a92849fa9a7ff6b5d7fcf3b6ff00c0583ff8dd6657d21fb2bfecf97dfb4cfc5ed33e1569fab5b68d36a36f7b3add5d4325c44a2ce1f3482913c6c4b740430c1eb9a00f02fed6baff009e76dff80b07ff001ba3fb5aebfe79db7fe02c1ffc6ebf73bfe1c97e31ff00a28da0ff00e0aaf7ff00932bf33bf6bffd98350fd94be265bfc39d4b5ab4d7269b48b4d57ed1676f2dbc616ee5b88c26c96495b2bf67249dd83b870307201f317f6b5d7fcf3b6ffc0583ff008dd1fdad75ff003cedbff0160ffe37599450069ff6b5d7fcf3b6ff00c0583ff8dd1fdad75ff3cedbff000160ff00e3759952c3119a68e10706460b9f4c9c5005efed6baff9e76dff0080b07ff1bafaeff610ba7bafdadbe15f9b1c20a789ed30638638cf31cddd154f61f957d6bf017fe0941e26f8e1f083c2ff0015ec7c73a3e9d07892cbed896971a75d4b2c20bb2ed674b98d58fcbd4281ed5efde19ff826febbfb1e6b36ff00b4eebfe32d375bd2be19193c4d7ba669fa75c437779058452178a2927ba78d5cab1dbb86338c9033401fbf58afe7d7fe0b6dff0021ef865ff608d77ff4af4eaf7c93fe0b37f056291a293c0de2657425581bad2b208ea3fe3f2bcffc7be15b1ff82b84767e2bf87378fe04b5f8762e349bc875fb75bd6bc7d57ecf748f09b1ba0aab1ac001258e4b630319a00fe74c120823823a1ad05d52e514284b7200039b6849e3dca64d7ee87fc392fc639e7e23683ff82abdff00e4cafc55f88fe0e97c01e37d73c1b35c25d3e8ba9dfe9cd346a51246b1ba96d99d5589203988b0049201009279a00e5a5d46e26428cb0a83de38228dbfefa55079efcf35468a2803fad7ff0082527fc9a1e9dff630ebdffa56d5fa4b5f9b5ff04a4ff9343d3bfec61d7bff004adabf496800a28a2800a283c0cd7e617c6cff0082a37c2df81df147c45f0b3c41e10d7efaf7c3b76b6735d5b4fa7c704aed0c53e6313dcc72602ccb9caf5a00fd3da4c57e61fc13ff0082a37c2df8e3f147c3bf0b3c3fe10d7ec6f7c4576d670dd5ccfa7c9044eb0cb3e64105cc9260ac2d8c2f5afd3d1c8cd007f3cfff0005af7f27c6df0de6548dd9341d58af991ac8013796632038233824671debf0eac6fa5b99658668adca9b6b93c5b42a41585c820840410464115fb85ff05b5ff91c7e1cff00d80755ff00d2db3afc31d27fe3e64ffaf6baff00d10f401fde2f83b8f0968a076d3ad3ff0044a57495cdf83bfe453d17fec1f69ffa252bc9ff00692f8fda0fecd5f0c66f8a1e24d2ef758b282facec4db69ed12ce5ef64f2d0833bc6980c4672c38a00f7ca315f8ddff0f9ff00825ff423f897ff0002b4affe4ca3fe1f3ff04bfe847f12ff00e05695ff00c99401faa9f153fe498f8bff00ec03a9ff00e93495fc26dfff00af5ffae307fe8a5afe9853fe0abbf097e2d67e17e8de0ef10da5ef8bca787edee679f4e921b79b5665b38a59562ba790c6924ca5b6a938ce057ce8dff044ef1ab85f33e24684eca889b8e937809d8a14138bc03381ce00a00fc1ca2bf787fe1c97e31ffa28da0ffe0aaf7ff9328ff8725f8c7fe8a3683ff82abdff00e4ca00fc1ea2bd7be3b7c2ab8f829f15fc4ff0c6eafa2d4a5f0e6a52e9ef75046f1472b44a8db951d9d947cf8c16278eb5adfb387c17bafda03e2ff873e14d9ea3069537882ea4b64bbb889e68a231db4f739648dd19811015c061cb039c02080785d7f4e5ff000460e7e01f8d7fec6d4ffd355857cddff0e4bf18ff00d146d07ff0557bff00c995fa89fb0efecabacfec99f0ff005ff05eb3af59f8824d635a1aa47359db4b6a9120b482d846565924627f71bb3bbbf4a00fb4e4fb8dc9e87f957f087f137fe47dd7ff00ec2ba8ff00e95cd5fddecbfeadbe87f957f085f137fe47dd7ffec2ba8ffe95cd401fa71ff046bff93a3bdffb13f58ffd2ad3abfa88afe5dffe08d7ff0027477bff00627eb1ff00a55a757f511401fc9cff00c156a6fb1fed97e2b9218a1dd269da1ee2f0c72127ec6067e753ce0019f403d057c95fb355f4b73fb42fc308e48e003fe135f0df290448c3fe2676fd1950115fbf1fb5c7fc132fc55fb4c7c70d67e2cd8f8d74ad22d352b6b0b78ecaeb4fb99e58fec70088b1922b8894ee6c9e9c0c77ce7e79d1ff00e0945e27f811aad8fc6cd43c73a4ea569f0feeedfc55716369a6dcc5717716892adf3c113cb76d1a3cab09456705416c9e2803fa091457e362ff00c1683e0932abff00c20de270180601ae74b5386191906f011c1e879afb0bf64dfdb5fc11fb5c5d789ed3c1fa0ea9a33785e3b192e0ea32dac82517e6709b3ecd34b8dbe4367763a8c679c007da3451450014514500145145007fffd5fdfca28a2800a28a2800a28a2803f9fbff0082de5add5d6aff0007c5b4324c56d7c4b911a96c65f4eeb806bf063fb2356ff9f2b9ff00bf4ffe15fdc9fc4bf815f07be3249a7cdf14fc21a478a5f4a5996c8ea96cb706dc5c143288f77ddde517763aed1e95e5ff00f0c3ff00b227fd123f09ff00e0b62a00fe2f3fb2356ff9f2b9ff00bf4ffe15fa51ff0004a2b4bbb5fdb23c2c2e619212da76b84798857205a7b815fd0dff00c30ffec89ff448fc27ff0082d8abacf03fecb7fb3bfc34f125b78c7c01f0f3c3fa06b766b22dbdfd859243711acc863902baf23723153ec6803df2bf97eff82c65add5d7ed4966b6b0c9315f0768d911a96c7fa56a5d700d7f5035e21f10bf66cf80bf1635e4f147c4af02687e25d5a3b74b45bcd4acd2e265b789999230cdc855676207a93401fc41ff00646adff3e573ff007e9ffc28fec8d5bfe7cae7fefd3ff857f687ff000c3ffb227fd123f09ffe0b62a3fe187ff644ff00a247e13ffc16c5401fc5e7f646adff003e573ff7e9ff00c2ad59693aaadedb9365700095092627f51ed5fd9cff00c30ffec89ff448fc27ff0082d8a90fec3ffb22019ff8547e13ff00c16c5fe1401cff00fc13e881fb1a7c2ac9ff009820ff00d1d25757fb6991ff000c95f17b9ff993758ffd267afc1efdad3f6a9f8e3fb36fc7cf17fc1cf831e2abdf09f827c357b05a68da1e9715947676303d8dadc324625b595c0696776c6ec0ce0002b0ff00669fdb03f683f8f3f1c7c19f093e2978cb50f117843c55abc5a56b7a3dfc764f6b7f63711cbe641288ed227d8fb40601864641eb401f97daae93aab6a776cb6770419e420889882371f6afe8b3fe08ad14b0fc36f8931cc8d1baeb5a582ac0820fd857a835fa11ff000c3ffb227fd123f09ffe0b62ff000af5bf86bf067e15fc1db5beb1f85be16d33c2f6fa948935dc5a65badba4d2460aab385e0900900d007a61e9f88afe20bf68dd3efee7e38f8f24b7b69a54ff0084a7c4237246cc32355bbee01afedfabe65d53f632fd9575bd4ef35ad5fe16785ef2ff0050b896eaeae26d3e379669e762f248ec7966662493dc9a00fe2bbfb2356ff9f2b9ff00bf4ffe147f646adff3e573ff007e9ffc2bfb43ff00861ffd913fe891f84fff0005b151ff000c3ffb227fd123f09ffe0b62ff000a00f03ff8252ab2fec8ba72b0208f10ebc083d47fa5b57e925715e00f871e05f859e1e4f09fc3bd0ecbc3da3472c93a58e9f10860592639760838058f26bb5a0028c81d68afcc2ff82a37c6cf8a3f03fe16f843c41f0b3c4577e1dbdbed7e7b6ba9acd61679608f4fb99c467cf8a6503cc8d4e76e6803f4ec9183cd7f1f5ff0518b2bcbafdaff00e25b5ac12cc175c504c68580ff008975875c034597fc1463f6bfbabc82d9be25eb8a269523242e9dc6e2067fe3c3debf7f3f67afd9ebe077c79f81de05f8c5f187c0ba0f8b3c69e2cd06c752d6b5ad4ac2296eefaee58943492300067000000000000000c5007e01ff00c139ecaf6d7f6bff00868d7304b086d718032232e7fe25d7fd32057f60c3a0af9e7c27fb267ecd7e03f11d8f8bfc19f0dfc3ba2eb5a64865b3bfb2b1486e2072a509475e412ac41f635f43d007f3d3ff0005ae827b9f1a7c398ede3795ce83aa9da8a58f17b67d866bf0e74ed33528669659ad27445b5ba259a36007ee5fa922bfb7ef893f00be0c7c62bbb2bff8a5e0dd1fc5371a6c6f0da49a9db2dc3411c8c19d537740c40271d715e69ff0c3ff00b227fd123f09ff00e0b62a00fa13c1c40f09e8bcff00cc3ad3ff0044a57e7f7fc156c83fb21ea3ff00630e83ff00a56b5f89ff00137f6f7fdab7c17e3ef10f85f44f88bac5be9da5eafa9d959dbc4b62120b6b4bc9ade18d77d9bb9091c6a32cc49ea4d7817c4ffdb3bf68af8c1e1397c15f107c69a9eb5a3cd3c372f6b742d04665b76df1b7ee6d617ca3608f9b191c83401f2b51451401ee5fb367fc974f01ff00d8d1e1ff00fd3ada57f71008f5ee6bf821f0df88b56f09eb963e22d0ae1ed6ff004eb982eeda64c6e8e7b6916689c6e0cb94911586411903208c83f679ff0082907ed804e7fe1656b7ff007ce9dffc81401fd88641a5afc64ff8255fed1df19fe3d6bde3f8fe2af8aafbc450697a6e913d94778b6e3c892e2e2f5252a60821cee5893ef038c57ecdd007f1a5fb78d8dedd7ed6bf14dadade5940f13dd826346600f9507a035d77fc1372caf2d7f6c6f868d7504b086d5ae403221504ff0065ea1d32057f4e7e25fd913f664f196bf7fe2af157c33f0deababea93b5cde5eddd824b3cf33fde7776e4935f347ed41f01be0c7ecf1f00bc6bf1a3e09f82742f0778e3c2ba6b5e68dae699610c777653b3ac6cf1b32b0f9a3764604105588ef401fa4208c0e69d907a57f1e971ff051bfdb0219e4847c4bd7088dd973b74ee7071ff3e15fbabff04b9f8d7f13fe38fc21f16788fe29f886efc457f65e245b3b59ef161578adce9f6937963c88a1523cc958e76e79eb401fa672ff00ab6fa1fe55fc217c4dff0091f75fff00b0aea3ff00a57357f77b2ffab6fa1fe55fc217c4dff91f75ff00fb0aea3ffa5735007e9bff00c11b3fe4e8ef7fec4fd63ff4ab4eafea2323d6bf85bf843f1afe23fc0cf12c9e2df865addd685aa4b692d8b5cda7926436f3b46ee9fbf8a64c334484fcb9ca8c11ce7e955ff828f7ed80cc07fc2cad7393fddd3bff00902803fb10eb5e4df1effe486fc43ffb15759ffd2396bc17fe09f5f123c6df16bf659f0c78ebe21ead3eb9aedf5e6b31cf7b702359244b6d46e21881112469f2c68abc28ce2bdebe3dff00c90df887ff0062b6b3ff00a472d007f0c973feb07fd738ff00f4015fbd3ff043cff90b7c61ff00af5f0cff00e85a8d7e0b5cff00ac1ff5ce3ffd0057ef4ffc10effe42df187febd7c33ffa16a3401fd05d14514005145140051451401fffd6fdfca28a2800a28a2800a28a2800a28a2800a28a2800af10f885fb49fc05f84faf2785fe2578ef43f0d6ad25ba5dad9ea57896f335bcacca92056e4ab323007d41af6fafe5fbfe0b1977756bfb52599b59a484b783b46c98d8ae7175a975c11401fbb3ff000dc1fb227fd15cf09ffe0ca2a3fe1b83f644ff00a2b9e13ffc19455fc5e7f6beadff003fb73ff7f5ff00c68fed7d5bfe7f6e7fefebff008d007f687ff0dc1fb227fd15cf09ff00e0ca2a43fb707ec88463fe16e784ff00f06517f8d7f17bfdafab7fcfedcffdfd7ff1ab565ab6aad7b6e0dedc1065404195fd47bd007eb17ed69fb2b7c71fda4be3e78bfe31fc18f0adef8b3c13e25bd82ef46d734b96ca4b3be812c6d6dd9e332dd44e42cb03ae76e0e320914cfd91ff0061cfda87e1afed1ff0f3c65e2af87dab58e8fa56bf6d757d773b59797040892a97223bb95c805c670a78cd7edcff00c13e803fb1a7c2ac8ff9820ffd1d257d9181e9400b5e47f12be3d7c1bf83b716169f14bc63a4785a6d512592cd354b95b73709095590a6efbdb4b2e71d322bd72bf9f8ff0082d84f3dbf887e18c96f23c4e347d746e462a79bbd3bb8a00fd5ff00f86e0fd913fe8ae784ff00f065151ff0dc1fb227fd15cf09ff00e0ca2afe2f3fb5f56ff9fdb9ff00bfaffe347f6beadff3fb73ff007f5ffc6803fb76f017ed39fb3ffc52f1147e12f875e3fd07c45accb1493a5969d7893ce628865df6af6507935eeb5fcaf7fc1232eeeaebf6b4d35aea69262ba0ebc01918b606cb5e9926bfaa1a00f0ef881fb4b7c03f853aff00fc22df123c79a1786f573047742cf52bc4826304a5824815baab15600fb1ae4ac3f6d2fd94755bfb6d2f4df8abe17babbbc9a3b7b7822d423792596560888aa392ccc4003d6bf05ffe0b0775756bfb5106b59a484b785f4204c6c5491e6ea5e8457e7a7c0bd42fee3e2e783e3b8b99a54ff8487443b5e466191a95af626803fb91afc6eff82cf7fc912f03ff00d8cd75ff00a6abcafd9015f8dfff00059eff009225e07ffb19aebff4d579401fcd16972245a95a4b2b05449e36663d000c0935fd5dfec8dfb5cfecc9e0bfd993e197853c55f133c35a56b1a5786ac2d6f6caeafe38a7b79e38f0f1c88795653c106bf936abd1ea7a9448b145773a228c2aac8c001ec01a00fed9fc27fb59fecd7e3cf11d8f843c19f123c3bad6b5a9c862b3b0b2be49ae2770a5c8445e490aa49f615f43d7f1f1ff0004e7bdbdbafdaffe1a2dccf2cc175c62048ecd8ff8975ff4c935fd830e82801690d2d1401fc97fc55fd803f6b4f147c47f136bba77c37d664b4bdd6b55b8b7911ac0ac90dc5ecf346e37dea300c8e0e0a822be7cf89ffb18fed15f07fc272f8d7e20f82f53d174786786d9eeae8da18c4b70db235fdcdd4cf976c01f2e327922bfb52c0f4afcdaff0082ad803f643d47fec61d07ff004ad6803f929a28a2800a28a2803f683fe0923f1afe12fc1fd5fe20dcfc50f16e93e178b51d334786d1b54b95b713c905cdf3c8a85bef145910b01d030f5afdb4ff0086e0fd913fe8ae784fff00065157f1676f77776a58dacd2425baf96e5738f5c11567fb5f56ff009fdb9ffbfaff00e3401fda1ffc3707ec89ff004573c27ff8328abc13f6a0f8f3f063f687f805e35f82ff0004fc6da178c7c71e2ad35acf46d0f4cbf864bbbd9d5d64648d59947cb1a33b12400aa4f6afe4dffb5f56ff009fdb9ffbfaff00e35f767fc1372f6f2ebf6c6f868b753cb305d5ae481239600ff65ea1d324d0053b8ff82727ed8134f24c3e1a6b8048ecd8dda771939ff9ff00afd73fd80b58d07f62df867e21f027ed47aad87c35d735bd746aba658ebf796f0cf7562b636b6c674114b2a14f3a175fbd918e40c8cfecb003038afe717fe0b53713dafc60f02c96d23c4dff0008b5c0dc8c54e0df0ee2803f645ff6dffd91594a8f8b9e13e41ff98945fe35fc707c41bcb4d43c67ad5ed8cab3c13ea37d2c7221caba49732ba303dc32b023d8d73dfdafab7fcfedcffdfd7ff1ace249393401eaff00087e0a7c47f8e7e2593c25f0cb44bad77548ad25be6b6b4f24482de068d1dff7f2c298569501f9b39618079c7d2abff04e1fdb01581ff856bae707fbda77ff0027d7d0ff00f046cff93a3bdffb13f58ffd2ad3abfa88c0f4a00f8b7fe09f5f0dfc6df097f659f0c7817e21e933e87aed8de6b324f65706369234b9d46e26889313c89f346eadc31c66be89f8cda56a7aefc21f1be89a25ac97da86a1e1dd56d6d2d622a249e79ad6448e34de55773b1006e60327920735e95d28a00fe3c1bfe09c9fb61ca11dfe19eb6adb10300fa7b005540383f6e19191e82bf5ebfe0947fb377c64f801a97c4b97e2b7862fbc3a9ae5be84962d786dcf9ed68d7be685fb3cf3fdd12a6776debc679c7eca607a52e00e9400514514005145140051451401ffd7fdfca28a2800a28a2800a28a2803f39ff6effdb835cfd8f6f7c176ba4786ac3c40be2a87549646bdbb9ad7c83a7b5b0017c9866ddbfed0739c636f7cd7e7e7fc3ed3c63ff44e741ffc1adeff00f21d687fc1713fe42df07bfebd7c4dff00a169d5f81dd781401fbc3ff0fb4f18ff00d139d07ff06b7bff00c875f4c7ec8fff000534f157ed31f1c346f84d7de0ad2b48b4d4adafee24bdb5d42e67963fb1c0650a2396de253b9b03af033df19fe65934cb9750e1edc06008cdcc20f3ea0b823e86bf49ff00e0949035afed95e158e5788b3e9dae60472a487fe3cffd866f43f91f43401fd62d7e7cfed3dff04f1f875fb51fc478be24f8afc4dad6937716956ba48b6b08aca480c56b24f22bff00a4c12b07267607040c01c57e82e696803f1bbfe1cc1f04bfe878f12ffe02e95ffc8747fc3983e097fd0f1e25ff00c05d2bff0090ebf6468cd007e377fc3983e097fd0f1e25ff00c05d2bff0090e957fe08c7f0523612278e7c4c194e4116ba57047fdb9d7ec8521e86803f02fc5bfb7ceadfb086b771fb28785fc2763e23d1be1c98b4ab2d5b55d42682f2ee39208af374b1dbdabc4a57ed3b3e5233b73819aeebe017fc158bc5ff001a7e31f843e184de03d1ac20f12ead0e9d2ddc1a95d4b240b2abb175492da356384200ddd4d7e5cffc1486d9eeff006c9f89ab1490864d56d4959268e36c1d2ec3070eca71c1e7dab92fd842d5ed7f6b6f857e6c9092fe27b4c08e68e43c473764663dc7e7401fd9757c53fb57fec47e08fdad753f0eea5e2ff10eada29f0e5b5e5ac31e9d1dac8b32dec90c8e641730cbca98176edc77afb5734b401f8dbff0e60f823919f1c7897aff00cfae95ff00c875fcee7c5bf08d97813e247893c21a74d25c5b68fac6a7a7452ca1448f1d8de4f6cacc10050ccb1066c00324e001815fdda1e9f88afe203f68ab47bbf8e5e3d7865b7c2f8a7c40ac1a789181fed4bb3c867047041e9d0d007db1ff000485ff0093b2d3bfec05aeff00e816d5fd5457f2b9ff00048d85adbf6b6d36291e2673a0eba711ca926014b6c6763363383d7d2bfaa2cd007f2e5ff058bff93a04ff00b16342ff00d1ba957e787c05ff0092bfe0ff00fb18344ffd395ad7e87ffc162c1ff86a04ff00b16342ff00d1ba957e787c04cffc2dff0007e3fe860d13ff004e56b401fdce8afc6eff0082cf7fc912f03ffd8cd75ffa6abcafd9006bf1bffe0b3dff00244bc0e7fea65baffd355e5007f3294515a09a65cba86dd02e7b3dc448c3eaace083ec45007b07ecedf196ebe027c5bf0e7c52b3d3e1d526f0fde9bc4b5b895e18a56304f06d678d5d946272d90adc8031824d7eb8ff00c3ed3c63ff0044e741ff00c1adefff0021d7e18ff64dd7fcf4b6ff00c0a83ff8e51fd9375ff3d2dbff0002a0ff00e39401fb9dff000fb4f18ffd139d07ff0006b7bffc8747fc3ed3c63ff44e741ffc1adeff00f21d7e13dc5a4d6d8f30c6c0f78e44900fa946600fd6abaab310aa0924e001d49a00fde0ff0087da78c7fe89ce83ff00835bdffe43af9dbf6a3ff829bf88bf694f84d73f0bb52f06e95a3c3717f637df6ab4bfb9b8903594a250bb25b78970c4609dd903a035f97034bb9600892d8679e6e611ff00b3d47369f71026f668587a47347237e48c4e3f0a00a55fb93fb1d7fc133fe16fed0df02349f89fe22f14eb9a65fdf5f6a96af6f670583c216c6ee4b756067b7964cb2c619b2d8c938006057e1b8eb5fd76ff00c12e3fe4cf7c3bff00618f10ff00e9cee2803e57f197fc11e7e0c7877c1faef882dfc6be24925d334dbbbc446b6d2c2b3410b48012b681b04af3820fa1afe706ea258650abd0a46ff4de81b1f866bfbb7f8aa40f85fe2f2c400341d4c927803fd1a4afe16ae74f9ae1a396292df69861eb710a9c88d410417041078208a00fd15ff827a7ec6de08fdacb50f17d978bf5bd4b471a058e9d750369d15ac8646bc9aea3657fb4c5280144008da01cb1c93c0afd3cff0087307c12ff00a1e3c4bff80ba57ff21d7847fc113a3f2bc45f13e12d1bb2691a106f2e4590026eb5138250919c10715fd03e6803f87cfda5fe17e93f06be37f8c7e1a687773df58f877579f4f82e2e96359a448d6360ce2255404ef23e55038e951fece1f1a2ebf67ff8bfe1cf8ad67a741aacde1fba92e52d2e25786294c96d3db619e34765004e5b214f2a0630491ea3fb7c67fe1adfe29678ff008a9aefff0045415f1fc30bcee238f6e4f76608a3eacc401f89a00fddcff87da78c7fe89ce83ff835bdff00e43af5af04fc39f0d7fc15974b9be30fc4796f3c0d77e0db86f0c5bd96852417b6f730491417e6691afad4b07dd385c050005ef935fceb7f64dd7fcf4b6ffc0a83ff008e57f4c3ff000462017e03f8dd032395f172a928eaeb91a5d88382a4838231c1a00aff00f0e60f825ff43c7897ff000174affe43a3fe1cc1f04bfe878f12ff00e02e95ff00c875fb23466803f3e7f661ff008278fc3afd973e23cbf127c29e26d6b56bb974abad24db5fc5651c022ba920919ffd1a0898b830281924609e2bf41a8a2803f1f3f6b8ff00829a78abf667f8e1acfc26b1f05695abda69b6d61711dedd6a17304b27db20129531c56f2a8dad91d7918ef9c7ccff00f0fb4f18ff00d139d07ff06b7bff00c875f2a7fc156a1fb67ed97e2b8e1961dd1e9da1ee0f3471907ec60e3e761ce0838f423d457e6cbe99728a5cbdb90a0938b9849e3d00724fd05007ee8ffc3ed3c63ff44e741ffc1adeff00f21d7e81fec21fb706b9fb615ef8d2d757f0d58787d7c2b0e972c6d657735d79e7506b904379d0c3b767d9c6319ceeed8afe46fa706bf7c7fe0877ff00216f8c3ff5ebe19ffd0b51a00fe82e8a28a0028a28a0028a28a00fffd0fdfca28a2800a28a2800a28a2803f9f4ff0082e27fc85be0f7fd7af89bff0042d3abf056dc0321cff724ff00d00d7f547ff0521fd8d7e2a7ed5fa87802e3e1b9d2847e1983588ef3fb4afdec8eebf6b431ecdb6d71bb0206ce76e38ebdbf317fe1cedfb51a07313f85c3947552dae4a402ca46481a70ce33d322803fa13f813e13f0bcff00047e1f4d368f60eefe17d19999ad622493671649253926be67ff0082935bdbf837f643f167883c251268ba9dbdf686b15ee9c3ec7731acba9db472049a0d9226f462adb5865491d09afb4be14f87b53f097c30f08f8535b112ea3a3685a6e9f7620732c427b6b78e290239552cbb94e18aae47381d2be33ff82a5ffc995f8cbfebff0040ff00d3ada5007f2dcdf1e7e2f0623fe130f10704ff00cc6f52ff00e4aafe9a7fe093fafebbe24fd976e354f116a579aa5db78a7554f3efae65ba97620842aef99ddb6a8e833fa939fe4e5fefb7d4d7ef57fc13c7f6ebfd9fff0067ef8012780fe235e6ad0eacdafea17fb2cf4b9eee210dc797b3f79182bbbe5391d45007efb7884b2e83a8b292a45a5c1041c107cb6ef5fc415c7c73f8bb6e628d7c63e216fdcc4c59b5bd4724b2024f1740753d862bfa60d5ff00e0aa5fb23dee937b690ea3e20f326b79a35ce857606e742064edf535fca1de4892caa53a2c51a1faaa007f51401fbbdff0476f1e78d3c67f173c6c9e28d7754d56287c330bc715f5fdd5e468e6f769655b89640ac400323b7e35fd0a57f27fff0004cbfda5fe16fecd9f10bc55e20f8a1717d05a6a9a145636e6c6ca4bd73325d79a772c592a369ea78cf15fb37ff0f5bfd90ffe823e21ff00c10ddff85007e845cf863c397b3bdd5e697653cd21cbc925b44eec718c962a49381debe6cfdaf345d2340fd97be2a6b7a1595b69da8d8f84f55b8b5bbb48520b88268e066492391155d1d4805594820f239af0aff87adfec87ff00411f10ff00e086effc2b88f895fb71fc00fda83e1ff88ff677f85d7faa3f8bbe22e9779e1dd15751d2ee6ceccdedf42e91f9d3ba911a0e4b1c12003804f1401fce4dff00c73f8b96f7d71047e2ff00106c8e5745ceb7a91385240ff97aafdfff00f823a78afc4de2df87df116f7c4dab5fead347ace9ab135fde4f78d1ab59862a8d3c923282c49c035f065d7fc11eff006a1bab996e59bc2ea65767206bb2e06e39ff00a06d7d95fb346a5a3ffc130341d63c21fb4edd086f3c77771ea5a30f0ea4faca1b7d3618ada7f3996084c6c1e44c65704370720d007ede573afe11f0b48ed249a3d833b92cccd6b1124939249dbc926bf3f3fe1eb7fb21ff00d047c43ff821bbff000a3fe1eb7fb21ffd047c43ff00821bbff0a00bff00f0525b5b7f087eca1afeb7e138d745d462d57428d2f34d1f62b9449b508239156683cb9143a3156dac32091debf978ff0085f3f17ffe870f107fe0eb52ff00e4aafe87be37fed11f0c3f6fdf871a87ecddfb3e5e5d4fe32d566b2d56dd35cb3b8d2ac8db691770dcdc16b878e4c1d80050118ee61c63247e78ff00c39d3f69ff00eff863ff0007b2ff00f2b6803f2cfc4be2df1178c2f86a7e25d46ef52ba08b1f9d797335d49b133b57cc9de47da3270376064e00c9ce2d9de5cd85cc7796723c3342eaf1c91b146564219486520820804104104641079af7cfda3ff670f1cfeccbe395f0178f5ac5b516b1b5bfff0040ba3771795766658ff78d0c0776607c8d9c71c9cf1f3d5007afff00c2faf8be79ff0084c3c41ff83bd4bff92ab9af137c4bf1d78c6d22b1f13ebba9ea9042e648e3bdd42eaed11c82a5956e269554ed2464007048ce0915c2d1401a1a400755b204641b88b20ffbe2bfb27fd8a3c2fe1bbcfd92be135cdde956334b278534d6791eda26662631c9254927debf8d4d3e64b6bfb6b8973b229a376c7270ac09afe953f661ff008290fecc5f0c7f67bf87df0fbc537fada6afe1fd02cac2f56df47b99e213c09b5c248836bae7a30e0d007ebc7fc21de13ffa0369ff00f8090fff001147fc21de13ff00a02e9dff008090ff00f115f1cfc32ff828a7ecd7f173c79a2fc37f065eeb52eb5afdc35ad9a5ce91716d099163794869640157e48d8fe15f755007f3a9ff0005a5d274bd2fc5ff000ed34db3b7b40fa16a858410a45922f6cc0276819c03fe735f87fa5806e5f233fe8d7279f685ebf743fe0b6bff00238fc39ffb00eabffa5b675f863a4ffc7cc9ff005ed75ffa21e803fba4f09784bc2b278574677d1b4f2c74fb4249b4879fdca7fb15f037fc153fc3ba069dfb24ea37161a6d9dbcbff090684bbe2b78e36c1ba5c8caa83835fa35e0eff914f45ffb07da7fe894af97ff006e8f81fe37fda17e01ddfc36f87df61fed79f56d32f57fb42e1ada0f2ace612c999163948240c0f90f26803f8c81d6bfaedff825c7fc99ef877fec31e21ffd39dc57e407fc39d3f69ffeff00863ff07b2fff002b6bf43be087ed11f0c3f602f871a7fecddfb41de5d41e32d2a6bdd56e1343b3b8d56c85b6af7735cdb95b848e3c9d84860514ee53c6304807ebdcb147346d14aa1d1d4ab2b004329e0820f04115cfff00c21de12ffa02e9dff8090fff00115f0bf877fe0a7bfb2a78a7c41a5f86748bfd79efb58beb5d3ed449a25cc6867bc95208833b00aa0bba8249c0cd7e865007e2cffc15ef51bef879e00f87ba87816e26f0f4f73abea31cf26912be9cf3469665d5247b5689d94300c0138cd7e047fc2f9f8bff00f4387883ff00075a97ff002557f4fdff000519fd963e26fed4be12f06e83f0d8e9825d0f52bdbabcfed3bc6b3531dc5b185446cb04f96dc7272bd07bf1f923ff000e74fda7ff00bfe18ffc1ecbff00cada00fca3d6358d4f5fd467d5b58b99af2f2e9cc934f712bcd2c8e7a9692466763eecc4fbd7db3ff04deb4b5bdfdb0be1bdbde431cf13eab72192540eac3fb32fce086041e403f515f41ffc39d3f69ffeff00863ff07b2fff002b6be94fd917fe09a9fb40fc0afda17c17f137c54de1f3a36857d3dcde7d975692e6e363d95d5ba88e336508277cc09cb8e01a00fdd41e0ef09e07fc4974effc0487ff0088ad6d3f4ad374a8da2d32d20b4476dccb044b1066c632420009c77abc3a0af94be3efed9df047f66bf11e9be16f8a375a9dbdfead64d7f6cb63a74d7a8d02c86224b440ed218743ea3d6803eab93ee37d0ff2afe24fc7bf1abe2be99e33d72ced7c5fe201147a9dfaa0fed9d4142aa5d4aaaa02dca8015400302bfa486ff82ad7ec86ca47f68f8879047fc806efff0089afe553c6faa5a6b7e2bd5b55b02cd6f777f793c4597692935c4922e4763b5864763c5007ebff00fc124be23f8efc63fb4dde59789bc41ab6a76abe12d5a416f7ba8ddddc3e625ce9e15f64f348a1943b00400704d7f4a75fc86ffc137be3f7c3afd9dbe3a5d78e3e25cf7906972787351d3d5acad24bc90cf733d9ba0f2e305b6ed85f2dd0703b8afdcdff0087adfec87ff411f10ffe086eff00c2803f42aefc35e1ebfb86babed32cee267c6e925b78e4738181966524e05790fc76f09f85e0f823f10668747b0474f0beb2cacb6b10208b39704109c115d3fc1af8c3e0af8f1f0fec3e267c3e96e66d0f5296e6181eeeddada62f6933dbca1a27f9971246c39eb8a83e3dff00c90df887ff0062b6b3ff00a472d007f0c770009063fb91ff00e802bf7abfe0877ff216f8c3ff005ebe19ff00d0b51afc16b9ff00583feb9c7ffa00afde9ff821dffc85be30ff00d7af867ff42d46803fa0ba28a2800a28a2800a28a2803fffd1fdfca28a2800a28a2800a28a2800a2bf27bfe0a5dfb60fc5ff00d95f51f87b6ff0bae74fb78fc4906b325f7db6c16f496b16b4116ccc91ede267cf5cf15f971ff0f7afdacffe823a17fe0893ff00926803faa8afcf4ff82a5ffc995f8cbfebff0040ff00d3ada57e317fc3debf6b3ffa08e85ff8224ffe49af26f8ddff000517fda0fe3dfc38d4be1878eaf3499b46d4e5b59664b5d292d65dd673a5c478944ef8c491ae7e5391c719a00f81dfefb7d4d3294924927bd7efdffc13a7f621fd9ebe3cfecf9278e7e24691a85e6aebe20d46c7cdb6d5af2ce3f22dc47b079704a89919396c64f73401f80983460d7f5dbff0eb8fd8f7fe85dd63ff000a1d4fff009228ff00875c7ec7bff42eeb1ff850ea7ffc91401fc88d15fb9dff000531fd8dfe03fecf3f08fc33e27f861a4df58ea1a8f885ac6e1eeb54bbbe5300b0ba9f012e257553e644a77000e0119c135f8716b1acb750c4fcabc8aa7e8481401060d7d8bfb03ffc9dbfc2dffb19ed3ff454f5fb2dfb1fff00c13e3f662f8a9fb357807e2178c743d4ee35ad734b1737b2c1ad5fdb46f2995c65628a648d0600e14015ea9f153f629fd9eff667f86de27fda07e15e8b7d6be31f87ba4def887439ef757bebdb78afeca17689a4b79a668e45ea0ab0e41238eb401fa9b5fcf9ff00c16dbfe43df0cbfec11ae7fe95e9d5f355d7fc15cff6b4b5b996d9b52d04989d9091a1260ed38ff9f9afb9bf652d3b4aff008297f8775bf177ed4f6a357bef03de45a6e8ada34b71a22c76da9431dcce245b59ff007859e34fbcc40da30064d007f37f83495fd777fc3ae7f63d183ff08eeb1d47fccc3a9fff002457f2cdf1bbc37a4f847e2b78b3c35a146d0d8699aeeaf656c8eed232c3697f710440bb659888e350589258f24e4d007dfdff000485ff0093b2d3bfec05aeff00e816d5fd54d7f0e9f007e3f78eff00672f1dc5f10be1ecb6b0eab0dadcda23dddb0bb8c47761049fbb2e992762e0eee39e0e6bee2ff87bd7ed67ff00411d0bff000449ff00c93401b7ff00058bff0093a05ffb16342ffd1ba957e48d7bd7ed09fb447c40fda4fc6a3c79f1166b59b531656d61bad2d45a47e4da195a31e5879390667c9ddce470315e0b40051457eb1ffc12fbf665f847fb46788fc69a77c53d3aeefe1d2349b0bab516b7f71625659ee6e2372cd6ef19605625c06240e48e49a00fc9dc1a4afebb4ffc12e3f63d033ff08eeb1ff850ea7ffc915fce0fedadf0d7c25f097f68ef1b780bc0f6d2dae8da36a82dad229a792e6458cd9da4d869652d239df2b9cb127040ce00a00ef3fe09c1ff002781f0d7fec36dff00a6ebfafec407415fc29fc1ef8b1e28f827f10747f891e0d7823d5b44b93756ad7108b8884862961cb4659370d933f1b873839e39fd04ff0087bd7ed658c7f68e85ff008224ff00e49a00fa3bfe0b69ff00238fc39ffb00eabffa5b675f863a4ffc7cc9ff005ed75ffa21ebe8efda47f6b3f8a9fb515fe8fa8fc4cb8b19e6d16da7b4b636564b64045712472b860249371dd1ae0e4606477af9c749ff008f993febdaebff00443d007f78be0eff00914f45ff00b07da7fe894ae92b9bf077fc8a7a2ffd83ed3ff44a57caff00b78fc6bf1dfc00fd9faefe22fc399ed6df598357d2ecd1ef2dc5d43e55dce239331965c9c1c8f985007d995fcab7fc15ebfe4ecb51ff00b01685ff00a05cd1ff000f7afdacff00e823a17fe0893ff926be1df8fdf1fbc77fb46f8ee5f885f10a5b59b559ad6dad1ded2d85a4663b40e23fdd877c11bdb27773c7031400bfb367fc974f01ff00d8d1e1ff00fd3ada57f7123a7e26bf82af06f8af54f04789f4bf15e8c516f749bdb5bfb732209104d673c7711165246e0248d4919191c646735fa5dff0f7afdacbfe823a17fe0893ff00926803faa8a2bf957ff87bd7ed67ff00411d0bff000449ff00c9347fc3debf6b3ffa08e85ff8224ffe49a00feaa28afe55ff00e1ef5fb59ffd04742ffc1127ff0024d7d35fb1effc147bf690f8e1fb457827e1b78c2ff479344d6efa7b7bd4b7d252da6289657570bb24133edf9e15cf1d3bd007f41d5fcdf7fc16c3fe4aef817fec569fff004b857f4803a0af97fe39fec79f033f68cf10e9de28f8a9a5df5fea1a5d93585ac96ba9ddd8aa40d2194a95b7910312c7249c9e07a0a00fe2970692bfaec7ff00825cfec7aaa4ff00c23bac700ffccc3a9fff002457f27fe39d32cf46f16eafa5d8294b7b5bfbc8225662c4243712468093c92154649e4f5a00e4e9e80ef5fa8afd0fff00826afc08f86ffb40fc78bbf057c4eb2b9bed2a3f0dea5a82c76b7935938b8b79ec9233e640c8f80b33e573839048e057eee7fc3ae3f63dff00a17758ff00c28753ff00e48a007ffc12d3fe4cafc1bff5ff00afff00e9d6eebeb2f8f7ff002437e21ffd8adacffe91cb5f851fb477ed59f137f609f8a1a87ecddfb3f3e9fa6f81b4082d6f2c2db51b36d56e966d595aeee4bdccf3891c34eeec371246ec7402bcd7c03ff052efda43e3478e3c3df08fc637ba3cde1ff1b6afa77873568adf4916b33e9fabdcc76772b1ce970cd1486295b6b804ab608a00fc85b907cc1ff5ce3ffd0057ef4ffc10f3fe42df187febd7c33ffa16a35f7da7fc12dbf63a44548fc37abaa280aaa3c41a98000e0003ed1c002be83f80ff00b2a7c1afd9b67d6ae7e1469b7961278812da3be377a8dd5fef5b33218b6fda647d9832be76e339e7a0a00fa368a28a0028a28a0028a28a00ffd2fdfca28a2800a28a2800a28a2803f0b3fe0b25f0dbc77f10755f850de0bf0feabadad8db78845c369ba75ddf884ccd61b049f66865d9bb636ddd8ce0e3a1afc48ff866cf8e9ff42278a3ff0009ed57ff00912bfb8ac5263ebf9d007f0edff0cd9f1d3fe844f147fe13daafff00225617897e097c56f0868f36bfe26f09ebda669d6e5165babdd1efed2043230440d2cf6f1c6a59880016049200c938afee9b1f5fcebf3d3fe0a9591fb15f8cc0cf37da07fe9dad2803f90fafeacffe0909ff00269d37fd8d7abffed1afe539fefb7d4d7f563ff0484ff934e9bfec6bd5ff00f68d007ea33ba468649085550496270001c924d70a3e2afc302323c5fa0ffe0cedbff8e5747e23ff00917f52ff00af3b8ffd16d5fc19dddd3c2d1c71a42079309e618c9c9452492572493401fd1bff00c161fc67e10f117c0af06db681ae69ba9cb1f8addde3b3bc8677553a5df2e488d988192067a64815fcde5910b7b6ecc7004a8493f514d96ea59976b88c0ff623443f9a819aaf401fd81fec13f10fc01a4fec81f0bf4ed57c4ba3d9ddc3a285960b8bf822950f9b270c8ce181f622ba9fdb1be237c3dd4bf655f8b361a7789f46bab99fc21abc71430ea16f24923b5bb00aaaae4b127a00326bf8dd4bc9a3508162207768a363f892a49a56bd9d94ae2219eeb0c6a47d085c8fc28024d5595f53bb6520833c8411c82371afe85ffe08d7e30f097873e1c7c4487c41ade9da64936b1a6b469797715bb385b2504a89194900f048ef5fcecd4f15c490821021cff7e357fcb7038fc2803fbb83f153e1863fe46fd07ff0676dff00c72bf90bf8e9f02fe2ff0089fe2ff8d35bd13c19e23bbb0bbf11eb93db5cc1a1ea33c13c13ea3732c52c52c56cf1c91c91bab2b2b10c0820d7c936b7524cef1c8909530cc7886307223620821720835fdd57c2b1ff0016c7c21d7fe403a677ff00a768e803f895f127c11f8ade11d264d77c4be13d774cb089911ee6f747bfb4855a46da80cb3dbc7182cc40505b2c4e064f15e555fd777fc151b8fd8f7c458cff00c863c3dffa73b7afe448f5a00f51f0cfc15f8a7e32d2975bf0c78535cd52c5dde35b8b1d22fef212f19c32896dede48f2a78237641e0806b765fd9c3e384313cd2f817c4ea91ab3b31f0fea80055192493680000773c0afe9a3fe0949cfec87a775ff91875effd2b6afd01f1871e13d648cffc83eefbff00d317a00fe0a9d1e3628e30460fe07907dc11d0f7afdc1ff8232f8a3c37e1bf16fc4397c43ab58e9892e87a5a46d79731db876179764853232e48041207622bf13b56ff008f98ff00ebdad7ff004425528a7921cec0a73d9d15c7e0181c5007f7727e2a7c31c1c78bb41ffc19db7ff1cafe5c3f6ebf84bf127e21fed47f103c51e0af0c6b7ade8d7fab24f67a8e9ba4df5f595d446c6cd37c3716d04b148bbe3652558e0a915f9d3a65dc936a56914b1c0c8f3c6ac0c116082c011f76bfb39fd87c67f644f8479ffa14f4dffd142803f91eff00866cf8e9ff0042278a3ff09ed57ff9128ff866cf8e9ff42278a3ff0009ed57ff00912bfb89c7d7f3a31f5fce803f83bf18fc38f1bf8025b787c65a1ea7a2bdd23490aea3617562d22210acc8b731445802402541009009c915cde93ff1f327fd7b5d7fe887afdceff82da7fc8e3f0e47fd40755ffd2db3afc31d27fe3e64ff00af6baffd10f401fde2f83bfe453d17fec1f69ffa252be17ff829ef877c41e29fd952ff0048f0ce977dac5f3ebda2482d74fb596f27291dc867611408ee42a82490a702bee8f077fc8a7a2ffd83ed3ff44a5749401fc3affc3367c74ffa113c51ff0084f6abff00c895e67e2bf06f89fc11aa1d1bc57a5dee937aa8921b7bfb59ece609202558c77091c80360e095c1c1c13835fdeae3ebf9d7f2b1ff00057aff0093b2d47fec05a17fe8175401f95745145001451450015f72ff00c139b54d3747fdaf7e1cdfead770595b43aa5c34935cc8b14680e997eb96672140c90393d4815f0d53e391a270e98c8ecc0303f504106803fbbc1f153e18607fc55fa0ff00e0cedbff008e52ff00c2d4f861ff00437e83ff00833b6ffe395fc267dbe7feec3ff7e22ffe268fb7cffdd87fefc45ffc4d007f76127c53f8625180f17683d0ff00cc4edbd3feba57f0f5f12258a6f1cebb2c2eb2236a9a832b210ca41ba988208e0820e47b5729f6f9ff00bb0ffdf88bff0089aa6492726803f5cffe08d7ff0027477bff00627eb1ff00a55a757f5115fcbbff00c11aff00e4e8ef7fec4fd63ff4ab4eafea22803f934ff82b27fc9e3f8a3fec1da1ff00e9257c87fb31ff00c9c47c30ff00b1d7c37ffa72b7afaf3fe0ac9ff278fe28ff00b07687ff00a495f21fecc7ff002711f0c3fec75f0dff00e9cade803fb87145028a0028a28a0028a28a0028a28a00ffd3fdfca28a2800a28a2800a28a2800a28a2800af9c3f6b0f81579fb48fc0fd6be11586af168536ad71a74e2fa7b76ba8e3fb0ddc374418924899b7f95b78718ce6be8fa2803f9f43ff00044ad7c9c9f89ba473ff00502bbffe58d7eacfec69fb376a3fb2cfc239be18ea5aedbf88647d62f3535bbb7b57b340b7623fddf96f2cc72a50f3bb90477193f59514014354b56bfd3aeac55821b882488311900ba95ce3db35f8087fe0899e22709e6fc4fd25d9111371d0aeb24228504e351033c7602bfa0aa2803f9f4ff8724ebdff00453748ff00c115dfff002c68ff008724ebdff453748ffc115dff00f2c6bfa0ba2803f9f4ff008724ebdff453748ffc115dff00f2c68ff8724ebdff00453748ff00c115dfff002c6bfa0ba2803f9f4ff8724ebdff00453748ff00c115dfff002c6be01fdb5bf629bdfd90ef7c336779e26b4f113788ad2fae95ad6c65b2108b296de22ac25b8b8dc5bed00820ae369e0e411fd84d7f3f1ff05b0826b8f10fc318ede3691ce8fae9da80b1ff008fbd3bb0a00fc11b7944321723394913fefb52b9fc335fd0ff0083ff00e0b29f0dfc39e12d13c3f37c3bd626934cd3ad2cde45d4ac943b5bc4b19600b640257383cd7f3d5fd93aaffcf9cfff007edbfc28fec9d57fe7ce7ffbf6dfe1401fd17ea1fb56f87bfe0a5fa55d7ecb1e10d12f3c0f7dab88b585d6b52961d4ada35d12e20ba68cc16d246ec64f957efae01272718af30ff8724ebdff00453748ff00c115dfff002c6be69ff82465adcdafed69a6adcc4f116d075e203a95cfc96deb5fd50d007cc1fb21fecfd7ff00b337c1bb7f859a96b506bf341a95fdf9bdb7b67b48c8bd97cdd822792561b338cef39afa2b5cb07d5347bed311c46d796d3401c8c8532a1404818ce335ab45007f3ecfff00044ef11cbb0cdf13f487748e38f71d0aeb2446a10138d440ce073803e94dff008724ebdff453748ffc115dff00f2c6bfa0ba2803f9f883fe089fe21b69a3b88fe26e8e1e26575ce8577d54e47fcc46bd5c7fc1417c1ffb1359d97ecade21f09ea5e26bdf8676969a04dacda5d5b59dbdf3c36d14be6a4333978c159572a4b60f426bf6bcf435fc77ff00c147ff00e4f03e257fd8717ff4dd61401fabdff0faef867ff44df5affc1a58ff00f1547fc3ebbe19ff00d137d6bff06963ff00c557f36d45007e8a7edf7fb65786ff006b7d6fc2daa787bc3b79a02e85a75e5948977730dc191ae678660ca61c801445820f3935f9ed65702da6672321a2963fa7988c99fc3766920b2bcba52f6d0492aa9c12885803f80a9ffb2755ff009f39ff00efdb7f85007f449a27fc167be1b695a35869aff0e75976b4b58202c353b10098e3552402d9e48ef5f547ecb1ff000519f097ed4bf137fe15ae83e0dd4b439469979a9fdb2eaf6dae232b66d02b4616125b71f3d4e4e07d7b7f267fd93aaffcf9cfff007edbfc2bf593fe08f96b736bfb5095b989e22de17d7480ea5723cdd3bd6803fa87afc99fdaff00fe09b1ad7ed47f192efe28daf8dec343b7b8d3ec2c858dce973ddba9b25906ff00323bb807cde61e369c60735facd45007f3ade29ff82316b9e1cf0ceafe217f893a4caba5d8dcde98d744ba56716f1b49b413a83004edc64838f435f865711793204ce7288e3e8ea1b1f866bfbbaf8a9ff24c3c5fff00601d4fff0049a4afe132ff00fd7aff00d7183ff452d007dcdfb14fec537bfb5e5ef89acecfc4d69e1d6f0eda58dd335d58cb7a2617b2dc4415445716fb4afd9c92496cee1c0c127efeff008724ebdff453748ffc115dff00f2c693fe0893ff0021ef89bff608d0ff00f4af51afe832803f9f4ff8724ebdff00453748ff00c115dfff002c68ff008724ebdff453748ffc115dff00f2c6bfa0ba2803f9f4ff008724ebdff453748ffc115dff00f2c6bf39bf6d2fd902f3f645f16e85e18bcf11daf885b59d2df5112dad9cb66b1049fc9d85659e72c4e7390c0638c57f6495fcdf7fc16c3fe4aef817fec579ff00f4b85007e2251451401fae9ff046bff93a3bdffb13f58ffd2ad3abfa88afe5dffe08d7ff0027477bff00627eb1ff00a55a757f511401fc9a7fc1593fe4f1fc51ff0060ed0fff00492be12f84be31b6f87df13bc25e39bcb77bb83c3dafe95abcb046ca8f2a585d4770c8acdf282c13009e013cf15f7a7fc1576d2eaebf6c8f148b685e52ba76864ec52d8cda7b57e6b3e97a922977b49955412498d8000773c5007f475ff0fadf867ff44df5affc1a58ff00f155f687ec7bfb6ff863f6bfbcf15da787bc337de1f3e158b4f9666bcba82e04c350338409e4938dbf6739cfa8afe39abf7c7fe0879ff216f8c3ff005ebe19ff00d0b51a00fe82e8a28a0028a28a0028a28a00ffd4fdfca28a2800a28a2800a28a2803f15bfe0adbf1f7e307c12d53e1847f0b7c57a9f86a3d62db5e6be5d3a558bcf6b56b1f28bee47cec123e31fdeafc71ff0086f8fdadbfe8a9789fff0002e3ff00e315fa59ff0005c4ff0090b7c1effaf5f137fe85a757e082a97385f427f00327f4a00fb0ff00e1be3f6b6ffa2a5e27ff00c0b8ff00f8c51ff0df1fb5b7fd152f13ff00e05c7ffc62b6742ff82777ed71e23d134ff10e93f0ef54b8b1d52d61bcb6992e34d0b243708248dc06be56c32b0232a0fa815abff0ed5fdb23fe89aeadff00813a5fff0027d00723ff000df1fb5b7fd152f13ffe05c7ff00c628ff0086f8fdadbfe8a9789fff0002e3ff00e315d77fc3b57f6c8ffa26bab7fe04e97ffc9f47fc3b57f6c8ff00a26bab7fe04e97ff00c9f401c8ff00c37c7ed6dff454bc4fff008171ff00f18a3fe1be3f6b6ffa2a5e27ff00c0b8ff00f8c575dff0ed5fdb23fe89aeadff00813a5fff0027d1ff000ed5fdb23fe89aeadff813a5ff00f27d00723ff0df1fb5b7fd152f13ff00e05c7ffc628ff86f8fdadbfe8a9789ff00f02e3ffe315d77fc3b57f6c8ff00a26bab7fe04e97ff00c9f49ff0ed5fdb23fe89aeadff00813a5fff0027d00725ff000df1fb5b7fd152f13ffe05c7ff00c62be98fd8f3f6c3fda5be20fed2df0e7c29e2bf88be20d4b48d4bc416d6d796773728f0cf0ba4ac51c2c4a482546466bf377e247c37f187c26f186a3e04f1de9d2e95ad6952ac377693346ef13bc693056685e48c931c88df2bb0c30e73903e8afd81ff00e4edfe16ff00d8cf69ff00a2a7a00fecdebcabe22fc0df83ff001767b1b9f8a1e0dd17c552e98924766fab5947766dd662a6411f980ed0e5549c75c0af55a2803e62ff00862cfd92bfe890f837ff0004f6dffc451ff0c59fb257fd121f06ff00e09edbff0088afa76be48d63f6effd91b40d5efb41d67e2668f69a869975359dddbcbe70786e2ddda39118797d55d483f4a00f9a7f6d6f857f0dbf668fd9ef5bf8a9fb3f786349f87be31b5bed22ca0d73c3d650d95fc56f7d7d0c3711ac8a9f7648d8ab02083dc702bf057fe1be3f6b6ffa2a5e27ff00c0b8ff00f8c57ecb7fc141bf6c0fd9abe2a7ecc5ae783be1ef8fb4bd735ab8d4f459e2b2b632195e3b6bf8659580280612352c79e82bf9a43401f62ffc37c7ed6dff00454bc4ff00f8171fff0018a3fe1be3f6b6ff00a2a5e27ffc0b8fff008c564fc2efd8a7f68ff8c9e128bc6ff0f3c157fad68d34f3db25ddbcd628865b76d922e27bb864cab020fc98f42457a27fc3b57f6c8ffa26bab7fe04e97ffc9f401c8ffc37c7ed6dff00454bc4ff00f8171fff0018a3fe1be3f6b6ff00a2a5e27ffc0b8fff008c575dff000ed5fdb23fe89aeadff813a5ff00f27d790fc62fd923e3d7c07d0ecbc45f147c277ba0586a172d696f35ccb6722c93244d31402dee67604468cdf30030a79cf0403b0ff86f8fdadbfe8a9789bff02e2ffe315f34f8e7c77e2bf891e26bef1878d353b9d5f57d4a5f3aeaf2edc3cd3481123dce40504ec455e00e14572914524f2a4110dcf230451ea58e00fcebec9f067ec09fb5478ffc29a4f8dbc27e02d4b51d1b5bb48ef6c6ee19f4f58e78261947512dec7200c39c322b7a81401f19515f66f8cff604fdaa3c01e14d5bc6de2cf016a5a768da25a497b7d7734fa7b4704108cbbb08af6490851ce1519bd01af8da58a482578251b5e3628c3d0a9c11f9d007f415ff0004a1f803f04be2bfc21f17ea9f12bc0da0f89ef2cbc416f6f6f71aad845752450b69b6b29446914955323b36071b989afd51ff00862cfd92bfe890f837ff0004f6dffc457c17ff000461ff009225e38ffb19ad7ff4d5675fb23401f317fc3167ec95ff004487c1bff827b6ff00e22bb4f017ece5f01fe176ba7c4ff0e7c03e1ff0d6ac6092d4dee97a7c56b398252a5e32f1a82558aa923a702bcb352fdbd3f642d1f52bbd2354f89da3db5ed8cf2dadcc1279caf14d03949118797d55948aed7e197ed5ff00b3c7c64f137fc21df0c3c6fa778875916d2de1b4b4f30b8b784aabb92c814052ea393ce78a00fa1e8a2be74f893fb5afece7f07fc512782be25f8e74df0feb714115cbd9ddf9a24114e098db2a8548600f43f5a00f4ff8a9ff0024c3c5ff00f601d4ff00f49a4afe132fff00d7affd7183ff00452d7f5edf10ff006f5fd90356f00f8974ad3be2868b35d5e68f7f6f044a65cbcb2c0ea8a3f77d4b10057f20d78eb24ca50e408a253f558d411f811401fbcdff000449ff0090f7c4dffb04687ffa57a8d7f4195fcf9ffc1127fe43df137fec11a1ff00e95ea35fd065007f2c1fb61fed87fb4b7c3efda5be237853c29f117c41a6e91a6f882e6dacecedae5121821448982206898800b1c0cd74bfb09fed71fb477c4cfda9be1ff84bc69f10b5ed5b46bfd4ae22bbb1bbb94782741a7de4aa1c2c484e1e35239c7147ed71fb09fed4ff00133f68ef885e34f097c3fd4aff0046d5b5eb9bbb1bb8ae34f093c0e912870b2de46e3250f054715d27ec41fb107ed3bf09bf69df01f8e3c71e03d474bd0b4bd46e27bdbd9ee2c1a38236b0bb8549586ee590e6495470a7ae4d007f4aa3a0afe6ff00fe0b61ff002577c0bff62bcfff00a5c2bfa401d057f37fff0005b0ff0092bbe05ffb15e7ff00d2e1401f88945145007a47c30f8b7f10fe0e7881fc51f0db5ebef0fea925acb66d7561208e5304cc8cf1e595c6d668d09e3f84735efcbfb7bfed6ccc17fe169789f938ff008fb8bff8c57897c1ef823f12be3bf89e4f087c2fd12e35dd562b39afdadadde08dc5bc0d1a3be6e268530ad2a023767e618079c7d36bff0004d6fdb21581ff00856bab7073ff001f3a5fff0027d007eddfec4bf097e17fed2bfb397877e2f7c7cf09e8de3ef1aeab75ab417baf6bd6105e5fdc47657f3db5bac92b2024450c688a30000a38af65f8cffb1ffecb5a2fc1ff001ceb1a4fc29f08da5ed8f87356b8b6b88749b749219a2b59591d182e559580208e845783fec87f1c3e137ec8ff0001741f817fb46789ac7c0de3bd16e353b9bed13517df71043a85f4f756cccd6fe6c444904a8e36c8d8ce0e0822bd9be227eda5fb2efc47f007897e1e781be21e93ac788fc51a45f68da3e9d6ed209af350d4217b7b6823de8abbe595d517730193c9039a00fe412e0059001c6510fe25413fad7ef57fc10eff00e42df187febd7c33ff00a16a35f01bff00c1367f6ca936b3fc33d555b622b0175a59195500e0fdbc6471e95faedff04a6fd9a3e34fecf9a97c4b9be2d785eefc3b1eb96fa125835d4b6b279ed68d7a660bf66b89f1b04a9f7b6e73c6707001fb23451450014514500145145007ffd5fdfca28a2800a28a2800a28a2803f9f4ff0082e27fc85be0f7fd7af89bff0042d3abf05adbfd61ff00ae727fe806bf7a7fe0b89ff216f83dff005ebe26ff00d0b4eafc16b6ff00587feb9c9ffa01a00fee6be0201ff0a37e1e7fd8aba37fe91c55eb5815e4df013fe486fc3cff00b15b46ff00d238abceff006c1f8e5aefece5f0135df8b7e1cd3ad355bfd26e34d863b5be67581c5f5e436cc58c7f30dab29231dc74a00fa77028c0afe6fcff00c16bfe2e8241f02f85b8ff00a6f7dfe147fc3ec3e2effd08be16ff00bff7dfe1401fd206051815fcdfff00c3ec3e2eff00d08be16ffbff007dfe149ff0fb0f8bbff422f85bfeff00df7f85007f483814d2060d7e59fec15fb7a78f3f6b8f1e788bc2de29f0ee8fa35ae8da3c7a8c52e9d25c3c9248f7021dade710028193c0f4e6bf534f43401fc7bffc14abfe4f23e257fd85adbff4d7a7d723fb03ff00c9dbfc2dff00b19ed3ff00454f5d7ffc14abfe4f23e257fd85adbff4d7a7d721fb03ff00c9dbfc2dff00b19ed3ff00454f401fd9bd145140087a7e22bf87dfda3eeae62f8e5e3c58a69117fe128f109c2b103fe42b77e86bfb823d3f115fc3b7ed27ff0025d3c79ff6347883ff004eb77401e2925d5d4abb259a4753d99891fa9a828a2803fad6ff00825201ff000c87a77fd8c3af7fe95b57e936057f27ff00b347fc14d3e217ecd9f0b6dfe17f87fc2ba16a76905f5edf0b8be96e9262f7b219597116570a4e077c75afab7c07ff000589f8b9e33f1a685e177f04f86618b55d52c2c659126bd2e91de5cc56ecca1b00b289323247f4a00fe84f02bf1bbfe0b3c07fc292f03ffd8cd75ffa6abcafd91afc6eff0082cf7fc912f03ffd8cd75ffa6abca00fe69747ff0090b597fd7cc5ff00a18afed0bf61f03fe1913e11ff00d8a7a6ff00e8a15fc59daced6b7315d2005a1916400f425483cd7eb6fc1fff0082b4fc4cf841f0bfc2ff000c349f077876f2cfc31a5dbe990dc5ccd7826952dd768770836863dc0e2803f77ff6e003fe1913e2e7fd8a7a97fe8a35fc5eeb1ff216bdff00af997ff4335fbc9f0eff00e0a0de3afdb63c4f6dfb2ef8cbc35a3687e1ff00890b73a06a3a8e9335c35fdb413dacf297805c0316ff00dce01756033f74d7bfc9ff000464f80f348d34be36f16b3c84b3129a56493c93ff001e14015bfe08c3ff00244bc71ff6335aff00e9aacebf640d7cbdfb2cfecabe10fd947c2dacf84fc1dac6a9acdb6b5a8a6a32cbaa0b71224890476e113ecd1429b3644bd5739cf35f511a00fe1a7e3bdd5cc5f17bc60914d222ff00c241adf0ac40ff009095d7a1afd0dff823ccd34dfb50b19a46908f0c6ba01624ff00cb5d3bd6bf3afe3d7fc95ff187fd8c1adffe9caeabf43ffe08e9ff002740ff00f62c6bbffa374da00fea3abf95bff82bb4d343fb59ea46191a32741d0812a48cfc973e95fd5257f2afff00057aff0093b2d47fec05a17fe8173401f96a6f6f581569e520f04173cfeb5568a2803f7c3fe0893ff21ef89bff00608d0fff004af51afe832bf8dbfd903f6d2f16fec8b79e22bcf0c685a5eb2de21b5b3b5946a2f3a0896ce59e5529e4e7258cec0e78c018afb8ff00e1f61f177fe845f0b7fdff00beff000a00fe903028c0f4afe703fe1f61f177fe845f0b7fdffbeff0a3fe1f61f177fe845f0b7fdffbeff0a00fe902bf9beff82d87fc95df02ff00d8af3ffe970a3fe1f61f177fe845f0b7fdff00beff000af817f6bcfdaf3c51fb5bf8a345f13f89f45d37469b46d35f4e8e3d39e6747479bcedcde772181e38e31401f1f51566d6159a4656e8b1c8ff00528a5b1f8e2bfa5ad2ff00e08d5f01afb4cb4bd7f1a78b15a782294809a5e01740c719b1cf7a00f83ffe08d9ff0027477bff00627eb1ff00a55a757f511815f007ecc9ff0004f2f863fb2dfc4597e24783fc49af6ab7b2e9775a51b7d4859080457524123b816d6d0b6f0605032c4633c57e805007f271ff00055f9a683f6c8f14986468f3a76879da4ae7fd13dabe46fd99eeae65fda1be18a4b348ea7c6be1bc866247fc84edfd4d7d6bff000564ff0093c7f147fd83b43ffd24af90ff00663ff9388f861ff63af86fff004e56f401fdc3003d2970074a051400514514005145140051451401ffd6fdfca28a2800a28a2800a28a2803f9f4ff0082e27fc85be0f7fd7af89bff0042d3abf05adbfd61ff00ae727fe806bf7a7fe0b89ff216f83dff005ebe26ff00d0b4eafc16b6ff00587feb9c9ffa01a00fee6fe027fc90df879ff62b68dffa47157c9bff00054bff00932bf197fd7fe81ffa75b4afacbe027fc90df879ff0062b68dff00a47157c9bff054bff932bf197fd7fe81ff00a75b4a00fe445fefb7d4d329eff7dbea69940051451401fb77ff00044fff0092bbe3affb15e0ff00d2e35fd201e86bf9bfff008227ff00c95df1d7fd8af07fe971afe900f43401fc7c7fc14abfe4f23e257fd85adbff004d7a7d721fb03ffc9dbfc2dffb19ed3ff454f5d7ff00c14abfe4f23e257fd85adbff004d7a7d721fb03ffc9dbfc2dffb19ed3ff454f401fd9bd145140087a7e22bf876fda4ff00e4ba78f3fec68f107fe9d6eebfb893d3f115fc3b7ed27ff25d3c79ff006347883ff4eb77401e25144d336d52a3dd9828fcce055afece9bfe7a41ff007fe3ff00e2abf4f7fe091d696b79fb5769f0ddc31cf19d0b5d25644575384b6c70c08afea5bfe11cd03fe81b67ff0080f1ff00f13401fc167f674dff003d20ff00bff1ff00f155eaff00032dcdb7c5df0734b243f3788744550b2a3127fb46d8f00127a027e95fdbeffc239a07fd036cff00f01e3ffe2681e1ed055832e9d680820822de3c823a1fbb401b22bf1bbfe0b3dff244bc0fff006335d7fe9aaf2bf646bf1bbfe0b3dff244bc0fff006335d7fe9aaf2803f994a28a2803eebff82707fc9e07c35ffb0e37fe9bafebfb101d057f1dff00f04e0ff93c0f86bff61c6ffd375fd7f6203a0a005a0d141a00fe18be3d7fc95ff187fd8c1adffe9caeabf43ffe08e9ff002740ff00f62c6bbffa374dafcf0f8f5ff257fc61ff006306b7ff00a72baafd0fff00823a7fc9d03ffd8b1aeffe8dd36803fa8eafe55ffe0af5ff002765a8ff00d80b42ff00d02e6bfaa8afe55ffe0af5ff002765a8ff00d80b42ff00d02e6803f2b554bb051d4d5dfecf9bfe7a41ff007fe3ff00e2abd87f66f4493e39781639155d5bc4fe1f04300410755b40410783915fdb97fc23ba01e4e9b67d4ffcbbc7ff00c4d007f04b35b3c001668db3fdc757c7d76938aaf5fd1dff00c168b4cd3ac3e1a7c396b2b582dcb6b3a9ee3144a84e2c5b192a066bf9d4d2403aa5983c8f3e3ffd08500469632ba860f0ae7b34a8a47d41208a24b296342e5e26c7659518fe001cd7f67dfb19687a2dc7ec9ff08e69ec2d6491fc1da396668236624db272495c935cafedfda268f6bfb1cfc539edac2d62917443b5d204561fbe8fa10b91401fc6fd156af862f6e07fd357ff00d08d55a00bda7ffad93feb84dffa2dabfbd5f0e7fc8034dffaf3b7ff00d16b5fc1569ffeb64ffae137fe8b6afef57c39ff00200d37febcedff00f45ad006c160a324e29be627f787e62bf317fe0ad9a8ea1a57ecaf05e699753d9cebe2ad2544b6d2bc326d613023746cad823debf97bff00859be3dffa0feabff831bbff00e3d401f7affc158c83fb63f8a31cff00c4bb43ff00d24af913f663ff009388f861ff0063af86ff00f4e56f5e37aaeafa96b778da86ab7335ddc3801a59e5799c851800bc8ccc703a64f15ec9fb31ff00c9c47c30ff00b1d7c37ffa72b7a00fee1c5140a2800a28a2800a28a2800a28a2803fffd7fdfca28a2800a28a2800a28a2803f9f4ff0082e27fc85be0f7fd7af89bff0042d3abf05adbfd61ff00ae727fe806bf7a7fe0b89ff216f83dff005ebe26ff00d0b4eafc16b6ff00587feb9c9ffa01a00fee6fe027fc90df879ff62b68dffa47157c9bff00054bff00932bf197fd7fe81ffa75b4afacbe027fc90df879ff0062b68dff00a47157c9bff054aff932cf197fd7fe81ff00a75b4a00fe445fefb7d4d32a5747dedf29ea7b537cb7fee9fca8019453fcb7fee9fca8f2dffba7f2a00fdb8ff8227ffc95df1d7fd8af07fe971afe900f435fce07fc114011f177c7408c7fc52d07fe971afe8fcf43401fc7c7fc14abfe4f23e257fd85adbff4d7a7d721fb03ff00c9dbfc2dff00b19ed3ff00454f5d7ffc14abfe4f23e257fd85adbff4d7a7d721fb03ff00c9dbfc2dff00b19ed3ff00454f401fd9bd7c9bfb48fed97f08bf659d4342d37e26c5ac3c9e21b7bab8b46d32cc5da84b378924f33f78854e665c7041f50719facabf9f3ff82db7fc87be197fd8235cff00d2bd3a803ec23ff057afd93b04987c5785058ffc4a3b2f27fe5b0ed5f9dde3aff82597ed1bf173c5dac7c4dd02ebc2b1e95e2ad46fb5cd3d6eb55b98ae05a6ab732dec2268d6c245491639c0755760181c3115f8d761febdbfeb8cff00fa29abfbb2f857ff0024c3c21ff601d33ff49a3a00fc85fd843fe09e7f1cff00669f8f167f11bc7973e1d9747834bd4acdd74dd427bab832de2c4130925a40bb47967277679e95fb6f451400552d4afe1d2f4fb9d4ae0318ad6192770a32db6252c7038e7038e6aed737e31ff914f5affb07ddff00e897a00fcd85ff0082bdfeca0c88ed6fe2c42e8afb5b4819018023a4c41e0f50483d8e2bf3c7fe0a33fb72fc13fda6be1a7867c2ff000dd35a5bdd2b5a9efae3fb46c45ac7e4c963716e36b798d96df2ae47a64d7e37eadff1f31ffd7b5aff00e884acd0a5bee827e94009453fcb7fee9fca98411c1a00fa83f63bf8afe15f82bfb41f837e23f8cc5d1d2343d48dd5d7d8e2f3e6f2cda5d43f226e5dc77ccbc67a64f6afe8487fc15ebf64ec7fa9f15ffe0a3ffb757f29a013c0a7f96ffdd3f95007f6dffb38fed3df0e3f6a2f0eeade28f86b16a715968d7eba75c7f6a5b0b591a668639c1450ee4aec917938e7a0c609fa28d7e377fc11878f827e38ff00b19ad7ff004d5675fb206803f863f8f5ff00257fc61ff6306b7ffa72baafa67fe09e7fb447c3ff00d9b7e36b78efe22aea0da61d1353b1ff008975b8b99bcebb7b468fe4dcbf2e207c9cf1c7ad7cd1f1e558fc5ff181009ff8a835bedff512baaf202ac39208fc2803fab0ff0087bd7ec9dff3c7c57ff828ff00edb5f1a7c7dfd97be217fc1463c663f695f81136956de0fd5acedf4ab78fc4575369da80b8d1e49ede72d0456d74a10bb1db970d81c819afc191d6bfaedff825c7fc99ef877fec31e21ffd39dc5007e597817fe0965fb46fc23f1768ff001375fbaf0ac9a5785751b1d73505b5d56e65b8369a55cc57b308636b08d5e468e021159d416232c057e888ff0082bd7ec9d80443e2bc300c3fe251d9b91ff2d8f6afd0ef8a9ff24c3c5fff00601d4fff0049a4afe132ff00fd7aff00d7183ff452d007ebff00fc148ff6d9f833fb4ff833c1da17c344d656e745d46f6eae7fb4ac85aa7973db185761f31b71dc791d857e42691ff215b3ff00aef1ff00e842b3c2b37dd04fd2b4b49471aad9fca7fd7c7dbfda1401fda67ec59ff2695f087fec4dd1ff00f4992b94ff0082837fc99a7c55ff00b021ff00d1d1d757fb167fc9a57c21ff00b13747ff00d264ae53fe0a0dff002669f157fec087ff004747401fc6b5ff00fc7f5c7fd757ff00d08d54abb7c8ff006db8f94ffad7edfed1aabe5bff0074fe54016f4fff005b27fd709bff0045b57f7abe1cff0090069bff005e76ff00fa2d6bf82cb04712c84a91fb89bb7fd336afef4fc39ff200d37febcedfff0045ad007e67ff00c15eff00e4d3a1ff00b1af48ff00dad5fca657f567ff00057aff00934e87fec6bd23ff006b57f29fe5bff74fe5400caf4ff82be2bd2bc0bf173c13e33d6fcd3a7e85e24d1f54bbf2137cbf67b2bc8a79762e46e6d88768cf278af33f2dff00ba7f2a3cb7fee9fca803fab0ff0087bd7ec9dda2f15ffe0a3ffb757d3bfb367ed8df08ff006a9b9f105a7c304d5964f0d476725eff0069d9fd97e5be3308b67cedbb981f3d31c57f165e5bff0074fe55fbdfff00043d046adf184118ff0045f0cffe85a8d007f415451450014514500145145007ffd0fdfca28a2800a28a2800a28a2803f9f4ff0082e27fc85be0f7fd7af89bff0042d3abf05adbfd61ff00ae727fe806bf7a7fe0b89ff216f83dff005ebe26ff00d0b4eafc15b72164249c7c920fcd4d007f739f013fe486fc3cff00b15b46ff00d238aaff00c5bf849e05f8e1e05bef86ff00122c64d4740d464b796e2de2b89ad5d9ed6649e222581d245db2229f95867183c578f7c0ef8e1f05ecbe0bf80acaf7c7be1982e2dfc33a4452c526af689247225a44acaca650432904107906bd4ffe17d7c0effa287e16ff00c1cd9fff001da00f933fe1d6bfb167fd09b7ff00f8506abffc9547fc3ad7f62cff00a136ff00ff000a0d57ff0092abeb3ff85f5f03bfe8a1f85bff0007367ffc768ff85f5f03bfe8a1f85bff0007367ffc76803e4cff00875afec59ff426dfff00e141aaff00f2551ff0eb5fd8b3fe84dbff00fc28355ffe4aafacff00e17d7c0eff00a287e16ffc1cd9ff00f1da3fe17d7c0eff00a287e16ffc1cd9ff00f1da00f3af81bfb1f7c04fd9cb5dd47c47f09342b8d26ff54b45b1ba926d4af2f83c0afe6050b73348ab86e72a01afa70f435e51ff000bebe077fd143f0b7fe0e6cfff008ed21f8f5f03b07fe2e1f85bff0007367ffc76803f950ff82957fc9e47c4affb0b5b7fe9af4fae43f607ff0093b7f85bff00633da7fe8a9eb67fe0a23aee89e23fdae3e226ade1fd42d753b1b8d52dde1b9b3992e2191469b6284a49196561b948c83d411dab1bf607ff0093b7f85bff00633da7fe8a9e803fb37afe7cff00e0b6dff21ef865ff00608d73ff004af4eafe832bf0abfe0b0df0d3e227c40d7be1c1f02f8575cf11a5b697ad453be8fa65d6a0b0bc97362c82436f1c9b0b2a315dd8ced38a00fe772391e26dc8704ab29fa302a7f435fa35a2ff00c153ff006bcd0b47b1d134ff001369e96ba7db4369029d16d18ac702045058f24800727ad7cac3f663fda209c7fc2b0f1ae4ff00d4b7a9ff00f23d78bea3a6dfe917b3e9baa5b4d67756d2490cd05c46d14b1c913147474601959594ab020104107906803fa0aff827c7edd5fb467ed0bfb42d8f813e24ebd697da14ba4eab74f6f06996f68e66b5584c67cc8c16c0f30f008f7cf6fde5afe4e7fe094be29f0cf84bf6a4d3f55f156af63a3592e89ad235c6a1731db421a44b7daa5e4655cb60e0679c1afe9bbfe17d7c0eff00a287e16ffc1cd9ff00f1da00f58ae6fc63ff00229eb5ff0060fbbffd12f5c5ff00c2faf81dff00450fc2dff839b3ff00e3b5cf78b3e3afc129bc2fabc30fc40f0bbbbd85d2aaaeb3664926170001e6f24d007f11fab7fc7cc7ff005ed6bffa212bf547fe097bfb327c1afda3bc47e34d3be2de8d3ead0e93a4d85d5a086feeac4a4b3dd5cc6e49b69622c0ac6a30d903191c935f957a9babdc2142180b7b65c839e5614047e07835fba3ff00044aff0091c7e237fd8074affd2dbca00fd0f3ff0004b5fd8af07fe28cbfff00c28355ff00e4aafe6fbf6d6f869e0df847fb4778dbc03e02b27b0d1347d505b59dbc93cb72c911b3b49b064999e46f9e573966279c670063fb4c3d0d7f289fb7e7c0cf8cfe2ffdabbe22eb3e19f00f8ab55d3ee7584960bcb0d0efeeada64363669ba39a281a371b9194e18e0822803e6efd8a7e1a7837e2e7ed1de09f00f8f6c9eff44d635436d796f1cf2db33c42ceee6c092164917e7890e5581e319c139fe9047fc12d7f62b23fe44cbfff00c28355ff00e4aafc3efd86be11fc51f863fb4df813c69f113c1fe21f0c787b49d524bbd4357d6b48bcd3b4eb2b74b1bc432dc5d5cc51c3126e915773b819615fd3a0f8f3f0380c1f885e16ff00c1cd9fff001da00c9f81bfb3bfc28fd9cf42d47c39f09b4b9b4ab0d56f05f5d473dedcdf33ceb1242183dcc92380234518040e3a57b7d793ff00c2faf81dff00450fc2dff839b3ff00e3b4c93e3f7c0b891a597e2278511114b3336b56602aa8c92499780075a00f98b5bff8266fec79e21d62fb5ed5bc237d35eea5753de5c38d77548c34d73234b23044ba0aa19dc9c280067815f0a7fc1403f614fd9a3e077ece97be3df86fe1abad3b5a8b58d26d1279756bfbb510dd5c08e51e5cf3c919254e01db91d4106bf77a19a2b8892781d648e450e8ea432b2b0c8208e0823a1afcfcff00829cf85fc4de2ffd95750d1bc25a3ea1ae6a0daee8b28b3d32d26bdb931c57219d845023c842a8c920702803f90c1d6bfaedff00825c7fc99ef877fec31e21ff00d39dc57f30dff0cc9fb43e7fe498f8d7ff0009bd4bff0091ebfa93ff00826bf86fc47e13fd93f40d17c55a4dfe89a8c7aaebb23d9ea56b2d95ca24da84ef1b3433aa48a1d1830ca8c839a00fae3e2a7fc930f17ffd80753ffd2692bf84cbff00f5ebff005c60ff00d14b5fdd9fc54ff9261e2fff00b00ea7ff00a4d257f0997ffebd7feb8c1ffa296803f58bfe0977fb2ffc17fda3b55f1cdafc5cd1a7d5a3d1b4ed2ae2cc437f75626392e6e2f2390936d2c45b2b0a0c3640c71824d7ec1aff00c12dbf62d460cbe0ebf041c823c41ab0208ffb7bafcd2ff8237f8f3c0fe0bd6fe23bf8c3c43a5686b71a568a909d4af61b412325d5f9609e6b2ee2a1d490338047ad7eef8f8f5f038f4f885e16ff00c1cd9fff001da00eb7c0be0af0efc37f0768de02f08dbb5a68be1fb2874ed3edda4799a2b6b75091a99242cef8500658927b9aa5f123e1d7853e2cf81f57f875e39b57bed075c83ecd7d6e9349034916e0d81244caebca8e54835d3e91abe95afe996dad687796fa8e9f7b124f6d776b2acd04f138cabc72212aeac390c090474a8f5bd7346f0de9771adf886fedb4cd3ad177dc5dde4c96f6f0ae40cbc9210aa3240c92393401f049ff00825b7ec584927c1b7e49e49ff848356ffe4ba3fe1d6bfb167fd09b7fff008506abff00c955f59ffc2faf81dff450bc2dff00839b3ffe3b47fc2faf81dff450fc2dff00839b3ffe3b401f24bffc12dbf62b08d9f065f9054823fe120d57904723fe3eba115f8bde26ff0082a2fed79e16d7f51f0f69fe27d3d6d34dbcb9b3b756d16d1d961b699e18c16206e3b5064e3935fd2a49f1e7e0714603e21785ba1ff98cd9ff00f1dafe253e234f05d78df5cb8b691258a4d4efd91d1832b2b5d4a4104704104107b83401fb41fb25fc71f1e7fc1437e245c7c0cfda8aea0f1278320d1eef5f8ecacedc69128d474f9ed63824fb459ba4a42adcc9f2ee009c120e057e8dff00c3ad7f62bffa136fff00f07faaff00f2557e417fc11aff00e4e8ef7fec4fd63ff4ab4eafea22803f3d3fe1d6bfb167fd09b7ff00f8506abffc9547fc3ad7f62cff00a136ff00ff000a0d57ff0092abf42eaadeded9e9b673ea1a84f1db5adb46f34f3cce238e28e31b99dd9880aaa012492001401f9fdff0eb5fd8b3fe84dbff00fc28355ffe4aaf7ff811fb297c11fd9ae7d6ae7e1068b3e912f8852d63d41a7d42eefbcd5b3329840fb54d2ecda667fbb8ce79e82bb31f1efe069191f10fc2b83c8235ab3ffe3b5d57863e20f813c6cd709e0df11e93af3598437034cbd86ecc224cec32089db6eeda719c67071d2803afa28a2800a28a2800a28a2803ffd1fdfca28a2800a28a2800a28a2803f9f4ff0082e27fc85be0f7fd7af89bff0042d3abf03abfaeefdba3f617bcfdb26f3c1b7769e328bc29ff0008a45a9c4cb2e98751fb4ff689b63918b9b7d9b3ecff00ed67776c73f01ffc38ef56ff00a2c36bff0084d37ff2c6803f0505c48a000138f58d0feb8a5fb4c9e91ffdfb4ff0afdeaff871deadff004586d7ff0009a6ff00e58d1ff0e3bd5bfe8b0daffe134dff00cb1a00fc15fb4c9e91ff00dfb4ff000a3ed327a47ff7ed3fc2bf7abfe1c77ab7fd161b5ffc269bff0096347fc38ef56ffa2c36bff84d37ff002c6803f057ed327a47ff007ed3fc28fb4c9e91ff00dfb4ff000afdeaff00871deadff4586d7ff09a6ffe58d1ff000e3bd5bfe8b0daff00e134dffcb1a00fc15fb4c9e91ffdfb4ff0a3ed327a47ff007ed3fc2bf7abfe1c77ab7fd161b5ff00c269bff96347fc38ef56ff00a2c36bff0084d37ff2c6803f03d98bb6e38fc0003f21c57d87fb03ff00c9dbfc2dff00b19ed3ff00454f5fa5bff0e3bd5bfe8b0daffe134dff00cb1af62f805ff0492d53e097c5ff0009fc5293e27db6af1f86b548b516b15d05ad5ae3ca575d825fb748133bfaec6e9401fb514628a2801a47d7a8afe1dff694ff0092e9e3cffb1a3c41ff00a75bbafee28f35f859f123fe08dbaafc41f1df883c66bf15ed6c975bd5751d496d8f879a6308bfbb9ae7cb2ff6f4ddb3cddbbb68ce3381d2803f9d84728772e3f1008fc8f1537da64f48ff00efda7f857ef57fc38ef56ffa2c36bff84d37ff002c68ff00871deadff4586d7ff09a6ffe58d007e0afda64f48ffefda7f8521b8908c613fefda7f857ef5ffc38ef56ff00a2c36bff0084d37ff2c68ff871deadff004586d7ff0009a6ff00e58d007e06f5e4d7ef0ffc112bfe471f88dff601d2bff4b6f2b47fe1c77ab7fd161b5ffc269bff009635f79fec3ffb085f7ec79ae789757bbf1a43e2a5f10585a592c7169874f307d9669a6dc49b99f7eef3718c0c63be7800fd17a4c52d1401f2b7edc031fb227c5cff00b14f52ff00d146bf8c2d525316a77714691aa24f22aa88930006200e95fdc77c75f86b27c64f83de2ff8570ea0ba4bf8ab49b9d2d6f5a13702dcdc2edf30c41d0bedebb772e7d457e2a5d7fc110b57bab99ae4fc60b453348d211ff08d39c6e39ffa08d007e077da64f48ffefda7f855ed3a4f3a7912548d97ecf727fd5a75585c83d3a82322bf773fe1c77ab7fd161b5ffc269bff0096353dbffc110b57b77671f182d4ee8e58ff00e45a71feb11933ff00211ed9cd007eeff83bfe453d17fec1d69ffa252ba4acbd16c0e95a4596965fcc3676d0c05c0c6ef2902671ce338cd6a500263ebf9d2d14500707f153fe49878bff00ec03a9ff00e93495fc265fff00af5ffae307fe8a5afef3bc5ba2b7897c2dac78712616e755d3eeac84c57788cdc44d1eedb919dbbb38c8cd7e0d49ff000440d62560cdf186d321113fe45a7e88a147fcc47d05007e06a48d1fddda73fde50dfcc1ad1d32532ea36b14891b23cd1ab03126082c323a57eeeffc38ef56ff00a2c36bff0084d37ff2c6a7b6ff0082216af6d7315cafc60b426275700f869f9da73ff411a00fd66fd8b3fe4d2be10ffd89ba3ffe9325727ff0506e7f633f8ab9ff00a021ff00d1d1d7bbfc11f87327c21f843e0ff85d2df8d51fc2ba2d9692d7ab11805c1b48c47e608cb3ecdd8ceddcd8f53591fb447c2697e3afc16f167c2487535d19fc4d63f6317ed01b9107ceafb8c41e32ff007718debf5a00fe1f2f2764bb9d11630ab23800469c004fb557fb4c9e91ff00dfb4ff000afdee97fe087fac4d2bcadf186d32ec58e3c34fd49cff00d046a3ff00871deadff4586d7ff09a6ffe58d007e0afda64f48ffefda7f8557afdf2ff00871deadff4586d7ff09a6ffe58d1ff000e3bd5bfe8b0daff00e134dffcb1a00f01ff008235ff00c9d1deff00d89fac7fe9569d5fd4457e587ec63ff04dfd43f64cf8af3fc4ab9f1fc1e278e6d16f349165168ed60c0ddcb6d2f986537738217ecf8dbb0677673c60fea7d0015e4df1ec03f033e2183c83e15d6baffd79cb5eb35c87c41f0c3f8dbc09e23f0647702cdb5ed26f74d17053cc109bb85e2de532bbb6eece32338c645007f07f34cd1b2aaac607971ffcb343d547a8afde6ff821f36fd5fe3092147fa2f867eea851f7f51ec00a56ff00821eeaee416f8c369c2aaffc8b4ffc200ffa08fb57df5fb0bfec2f79fb1b5e78caeeefc65178affe12b8b4c89562d30e9df66fece37272737371bf7fda3fd9c6def9e003f43a8a28a0028a28a0028a28a00fffd2fdfca28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2803ffd3fdfca28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2803ffd4fdfca28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2800a28a2803ffd9", + expectedOutput: "http://en.m.wikipedia.org", + recipeConfig: [ + { + "op": "From Hex", + "args": ["Space"] + }, + { + "op": "Parse QR Code", + "args": [false] + } + ] + }, + { + name: "Parse QR Code : PNG", + input: "89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 7d 00 00 00 7d 08 00 00 00 00 aa eb 33 f9 00 00 01 06 49 44 41 54 78 da ed da cb 0e c3 20 0c 44 51 fe ff a7 db 5d a4 54 d8 99 29 01 24 73 59 75 91 f8 b0 70 79 38 6e 9f 9d a3 a1 a3 a3 a3 af d6 db f3 c8 9f b3 a2 a0 a3 5f ef c5 e9 79 8f 2f fc ca a3 a0 a3 77 22 f4 72 b9 17 eb 37 a0 13 05 1d 5d 7c cf 7a 0e 1d 7d 46 ce e7 2b 32 3a fa 2b fb 7b b8 c8 2e 39 5d a0 17 d4 d5 bb 8c ba fa ce b9 49 a1 97 d1 e5 62 4b 94 ee 4b aa 46 e8 b5 f4 5e de 86 1b b5 ba e7 87 41 d1 d1 c5 ac 56 27 28 ac d2 e8 e8 e9 dd c3 af e0 c8 45 1e f4 d3 75 f5 b3 47 5e b7 b6 8a d7 e8 e8 cf b5 67 6b f5 f5 6e d0 e8 a7 eb ea 5d 26 9f a5 90 f8 e8 c7 eb 23 75 1b 61 5a 6f 76 bc a0 d7 d2 d5 de 03 6b 82 61 64 74 74 eb 4b 9a d3 e2 12 9f 0d d0 d1 ff ea 3d b0 56 5f 74 f4 51 5d a8 4c e7 7f 0b 74 74 bb f7 40 d8 f8 27 76 3e a0 97 d1 47 8e 8c d6 77 3b 74 f4 4d 03 1d 1d 1d 7d cd f8 02 b8 db 6e a8 48 1e c7 f1 00 00 00 00 49 45 4e 44 ae 42 60 82", + expectedOutput: "Hello world!", + recipeConfig: [ + { + "op": "From Hex", + "args": ["Space"] + }, + { + "op": "Parse QR Code", + "args": [true] + }, + ], + }, + { + name: "Parse QR Code : Transparent PNG", + input: "89504e470d0a1a0a0000000d49484452000000e8000000e80806000000e5a9a878000000017352474200aece1ce900000a49494441547801edddcb6e6db90d04d09ba0ffff97936b0499b20e4ccbb51fab8120035a22b5740a7b2024fdaf3f7ffefce7ef7f9efccfbf96874b3e69ffedfae5f8ebfb4de74bf3a5f3a7f5a9ff76ffd4bf5aff77b5bbe604088c02023af22812e80a0868d75f7702a380808e3c8a04ba0202daf5d79dc02820a0238f2281ae808076fd7527300afc3356ff574cef501f6c71f44fdaef60a9ffd6eff4feedf9d2f9b63f9eedf9b6fdd3faf1fcbea0894f9d405140408bf85a134802029a84d409140504b488af35812420a049489d405140408bf85a134802029a84d40914053e79074de38def3869f107f5f63b56ea7ffafc1f108d7f727afed3fb8f87fb81e2e9fb4b3ee3117c41471e45025d0101edfaeb4e601410d091479140574040bbfeba1318050474e45124d01510d0aebfee044601011d79140974057ee21db47b827df7f40e96deb1527d3fe1bc439a7f5efde74f9afff4fe69be57d77d415f7dfd0e7f750101bdfa0d99efd50202faeaeb77f8ab0b08e8d56fc87caf1610d0575fbfc35f5d4040af7e43e67bb58080befafa1dfeea02de41f33be0f60ed33b627a87dcf64ffb6fe7dbaedf9eefd1eb7d411f7dbd0e77770101bdfb0d9affd10202fae8eb75b8bb0b08e8dd6fd0fc8f1610d0475fafc3dd5d4040ef7e83e67fb480803efa7a1deeee023ff10e9aded9ae6e94def1d2fce9fca99efa6fd7a7f9533dcd97d65fbd9e7cabf3fb8256f93527300b08e8eca34aa02a20a0557ecd09cc02023afba812a80a0868955f7302b38080ce3eaa04aa02025ae5d79cc02cf0c93be8d3dfc166a1fcbf174d3e977e674b87ff5b4ff3a7f3a716697deabfdd3fadafd67d41abfc9a1398050474f65125501510d02abfe6046601019d7d5409540504b4caaf3981594040671f5502550101adf26b4e6016d8be31cdbbdfa39adee1d229b686dbfe69be544ff3a7f9b6ebb7f3a5f5b7aefb82defafa0cff7401017dfa0d3bdfad0504f4d6d767f8a70b08e8d36fd8f96e2d20a0b7be3ec33f5d40409f7ec3ce776b0101bdf5f519fee902e90debebfce91d2c197dd223ed31d5d37cdbfe69ff69b6afdad5fb5ffd7cc9eff4fc69ff345ffa7d8c755fd091479140574040bbfeba1318050474e45124d01510d0aebfee044601011d791409740504b4ebaf3b81514040471e45025d814ffe7f714f4f98de9952ffed3bd4b67f9a2fd553ff74beb43ef53fbd7feafff4faea7e7c419ffef370be5b0b08e8adafcff04f1710d0a7dfb0f3dd5a40406f7d7d867fba80803efd869defd602027aebeb33fcd30504f4e937ec7cb716486f603f71b8f40eb49d61bb7f5abf35d89e6fdb3fad4fe73f3dffe9fe57df7f9ccf1734fd7cd509140504b488af35812420a049489d405140408bf85a134802029a84d409140504b488af35812420a049489d4051e0eb8d6b7c87f95b3ffd0e76faf8e97ca97f3a7fda3fad4ffdb7f5f67ca9fff67cedf547efd717b47dbdfa1318040474c05122d01610d0f60de84f601010d0014789405b4040db37a03f81414040071c25026d01016ddf80fe040681af379cb7bf539d3effd177b2e16e3f2da5f39f9e3ff5fff41cdffdbb74be34dfd1f5bea0dfbd56eb08fc828080fe02b21604be2b20a0df95b38ec02f0808e82f206b41e0bb0202fa5d39eb08fc828080fe02b21604be2b20a0df95b38ec02f08fcc4bf1f74fb0eb43de6b6ff76fd76feedfaed3bdde9fe69ffe49fd6b7cf9fe64be71be7f7054dbcea048a02025ac4d79a401210d024a44ea02820a0457cad092401014d42ea048a02025ac4d79a401210d024a44ea02890de688aa37ddc7a7c47fabbcbddcfd83e5feafff1457df30fb7f797e64ffba7f5e958abfd7d4113af3a81a2808016f1b5269004043409a913280a0868115f6b0249404093903a81a2808016f1b5269004043409a913280aa4379a9f186dfb8e9466d89e613b5fea9ff6dfae3fed93f64fe74beb4fd76fedeb0b7afae7617f020b01015de0594ae0b480809e16b63f818580802ef02c25705a40404f0bdb9fc042404017789612382d20a0a785ed4f6021f0f546b47dc74aef4c8bf13e5a9ae6dfceb7ddfff4fa8f908a7f94fcdb3e69be2d5d3adfb8bf2fe8c8a348a02b20a05d7fdd098c02023af22812e80a0868d75f7702a380808e3c8a04ba0202daf5d79dc02820a0238f2281aec0d71b507aa749ef44edf549703bdf76ffb47e5b4ff793f6dffa6cd76fe74bebdb3e69bed1cf1734f1a913280a0868115f6b0249404093903a81a2808016f1b5269004043409a913280a0868115f6b0249404093903a81a2c0f68de86bf4f11da778b6ffb74e67dcce7f7affff9fe3bbffbd9d2fad4f7325dfedfea97faaa7f9d2fa544fe71bfbfb82265e75024501012de26b4d20090868125227501410d022bed604928080262175024501012de26b4d200908681252275014f87aa319df617e60b6f40e945aa4f9d2fea7d7a7f9b7f5edf9eedefff4fc69ffd3bf9ff17e7d41d3f5a813280a0868115f6b0249404093903a81a2808016f1b5269004043409a913280a0868115f6b0249404093903a81a2c0f806f3e15ca7df893e1ce3d89f25a3f6f9d37c09663bff76ffb4feeaf5e4bff2f505bdfaf59befd50202faeaeb77f8ab0b08e8d56fc87caf1610d0575fbfc35f5d4040af7e43e67bb58080befafa1dfeea02027af51b32dfab05d21bce1b70d23b5532681b6ee74fe74bf5edf9d3fcdbfdd3fca97f5a9fe65bedef0b9af8d509140504b488af35812420a049489d405140408bf85a134802029a84d409140504b488af35812420a049489d4051e09fbfbd57ef34c5d93f6d9ddea9523df5b9bbdff6fcc9e7743df99f3edfb6ffb8de17f4f4cfc7fe04160202bac0b394c06901013d2d6c7f020b01015de0594ae0b480809e16b63f818580802ef02c25705a40404f0bdb9fc042e0eb1d34fd73fa1d29f54ff5f11d292dfea0bedd7feb97faa7fdd3fa4490d69fee9fe64bfdd3fa6d3df55ff9f9826eafc77a02070504f420aead096c0504742b683d818302027a10d7d604b60202ba15b49ec04101013d886b6b025b0101dd0a5a4fe0a0c027efa0a97d7ae749eb533dbd33a5f5ed7af2d99eeff4fe57f73b3ddff67e56f3f982aef82c2670564040cffada9dc04a4040577c1613382b20a0677ded4e602520a02b3e8b099c1510d0b3be7627b01210d0159fc504ce0afcc43be8d909afbf7bf59decfa3c71c2e4b77de74debe380e10f8eeeef0b1af49509340504b4a9af37812020a0014899405340409bfa7a130802021a809409340504b4a9af37812020a00148994053c03b68fef7a3a677baedfd1d7d47fb3b5cda7f7bbeedfe69fdd6f7eaebc7f3fb825efdfaccf76a01017df5f53bfcd50504f4ea3764be570b08e8abafdfe1af2e20a057bf21f3bd5a40405f7dfd0e7f750101bdfa0d99efd5023ff10eba7d47bbfa058cef547f873f7dfeb4ffe9f9b6fdb7f79bfaa7fdb7eb4ffb8ef3fb828e3c8a04ba0202daf5d79dc02820a0238f2281ae808076fd7527300a08e8c8a348a02b20a05d7fdd098c02023af22812e80a7cbd11a5779eee84fbeea7dfc1f613ce3bb4e7dff69f4fb7fffda5f94effbe53ffd5f97d41139f3a81a2808016f1b5269004043409a913280a0868115f6b0249404093903a81a2808016f1b5269004043409a913280afc1753e7a9d79c5c60f40000000049454e44ae426082", + expectedOutput: "http://en.m.wikipedia.org", + recipeConfig: [ + { + "op": "From Hex", + "args": ["Space"] + }, + { + "op": "Parse QR Code", + "args": [true] + }, + ], + }, + { + name: "Parse QR Code : Angled code", + input: "89504e470d0a1a0a0000000d49484452000000d9000001010806000000f5a4008000000c45694343504943432050726f66696c65000048899557075853c9169e5b52496881084809bd89d2ab94105aa44a156c84249050624c082276655905d72e2260435744145d5d01592bf6b228f6fe5017959575b16043e54d0aaceb7eefbdef9d7c73ef7fcf9cf94fc9dcb93300e8d4f2a4d27c5417800249a12c31329435213d83457a0468f087003fe0c9e3cba5ec8484180065e8fe77797b035a42b9eaa2e4fa67ff7f153d8150ce07004980384b20e71740fc330078295f2a2b0480e80bf5d6330aa54a3c096203190c1062a912e7a871a91267a97195ca26399103f12e00c8341e4f960380762bd4b38af8399047fb16c4ae12815802800e19e220be882780380ae2510505d39418da0187acaf7872fec69935ccc9e3e50c63752e2a218789e5d27cdeccffb31cff5b0af215433eec60a389645189ca9c61dd6ee54d8b56621ac4bd92acb87888f5217e2f16a8ec2146a92245548ada1e35e5cb39b0668009b1ab8017160db129c41192fcb8188d3e2b5b1cc18518ce10b4585cc84dd68c5d2c94872769386b65d312e38770b68cc3d68c6de2c9547e95f6271579296c0dff2d91903bc4ffa644949ca68e19a3168953e320d6869829cf4b8a56db603625224edc908d4c91a88cdf06627fa1243254cd8f4dc99645246aec6505f2a17cb1c52231374e83ab0b45c9511a9e5d7c9e2a7e23885b851276ca108f503e2166281781302c5c9d3b76592849d1e48b75490b431335635f49f31334f63855981fa9d45b416c2a2f4ad28cc5830ae18454f3e371d2c28464759c78562e6f5c823a1ebc18c4000e08032ca0802d0b4c03b940dcd1dbd20b9fd43d11800764200708818b463334224dd52381d7245002fe804808e4c3e34255bd425004f59f87b5eaab0bc856f516a946e4812710178068900f9f15aa5192616fa9e037a811ffc33b1fc69a0f9bb2ef9f3a36d4c468348a215e96ce9025319c18468c2246101d71133c080fc063e035043677dc17f71b8af62f7bc2134227e111e13aa18b707baa78a1ec9b7c58201674410f119a9cb3bece19b783ac5e78281e08f92137cec44d800bee093db1f160e8db0b6a399ac895d97fcbfdb71cbeaabac68ee24a412923282114876f476a3b697b0db3286bfa7585d4b1660dd79533dcf3ad7fce579516c07bf4b796d8626c3f76063b8e9dc30e612d80851dc55ab18bd861251e9e45bfa966d190b744553c799047fc0f7f3c8d4f6525e5ae8dae3dae9fd47d85c262e5fa0838d3a43365e21c51218b0d577e218b2be18f1ec572777583abb6f23ba25ea65e3355df078479fe2fdda20b00049e1b1c1cfce52f5d743600fb4e0040fdcace7e9b7a2d3e5bcf57c88ad43a5c7921002ad0816f94313007d6c001e6e30ebc41000801e1601c8807c9201d4c815516c1f92c0333c06cb00094810ab002ac05d56013d80a7680dd601f680187c071701a5c0097c1757017ce9e6ef01cf481b7600041101242471888316281d822ce883be28b0421e1480c9288a42399480e224114c86c64115281ac42aa912d4803f2137210398e9c433a91dbc843a40779857c443194861aa066a81d3a06f545d968349a8c4e4673d0e968095a8a2e43abd03a7417da8c1e472fa0d7d12ef439da8f014c0b636296980be68b71b0782c03cbc664d85cac1cabc4eab026ac0dfecf57b12eac17fb80137106cec25de00c8ec253703e3e1d9f8b2fc5abf11d78337e12bf8a3fc4fbf02f043ac194e04cf0277009130839841984324225613be100e1147c9bba096f89442293684ff4816f633a3197388bb894b881b887788cd8497c4cec279148c624675220299ec4231592ca48eb49bb4847495748dda4f7642db205d99d1c41ce204bc80bc995e49de423e42be4a7e4018a2ec596e24f89a708283329cb29db286d944b946eca00558f6a4f0da4265373a90ba855d426ea29ea3dea6b2d2d2d2b2d3fadf15a62adf95a555a7bb5ce6a3dd4fa40d3a739d138b44934056d19ad9e768c769bf69a4ea7dbd143e819f442fa327a03fd04fd01fdbd36437bb436575ba03d4fbb46bb59fb8af60b1d8a8ead0e5b678a4e894ea5ce7e9d4b3abdba145d3b5d8e2e4f77ae6e8dee41dd9bbafd7a0c3d37bd78bd02bda57a3bf5cee93dd327e9dbe987eb0bf44bf5b7ea9fd07fccc018d60c0e83cf58c4d8c638c5e836201ad81b700d720d2a0c761b7418f419ea1b7a1aa61a161bd6181e36ec62624c3b269799cf5ccedcc7bcc1fc38c26c047b8470c492114d23ae8c786734d228c44868546eb4c7e8bad147639671b8719ef14ae316e3fb26b88993c9789319261b4d4e99f48e34181930923fb27ce4be91774c515327d344d359a65b4d2f9af69b999b459a49cdd69b9d30eb35679a8798e79aaf313f62de63c1b008b2105bacb1386af13bcb90c566e5b3aa5827597d96a69651960acb2d961d960356f65629560badf658ddb7a65afb5a675bafb16eb7eeb3b1b089b5996dd36873c79662eb6b2bb25d677bc6f69d9dbd5d9addf7762d76cfec8decb9f625f68df6f71ce80ec10ed31dea1cae39121d7d1df31c37385e76429dbc9c444e354e979c51676f67b1f306e7ce5184517ea324a3ea46dd74a1b9b05d8a5c1a5d1e8e668e8e19bd7074cbe817636cc6648c5939e6cc982fae5eaef9aedb5cefbae9bb8d735be8d6e6f6caddc99def5ee37ecd83ee11e131cfa3d5e3a5a7b3a7d073a3e72d2f8657acd7f75eed5e9fbd7dbc65de4dde3d3e363e993eb53e377d0d7c137c97fa9ef523f885facdf33be4f7c1dfdbbfd07f9fff9f012e0179013b039e8db51f2b1cbb6dece340ab405ee096c0ae20565066d0e6a0ae60cb605e705df0a310eb1041c8f690a76c47762e7b17fb45a86ba82cf440e83b8e3f670ee75818161619561ed611ae1f9e125e1dfe20c22a2227a231a22fd22b7256e4b128425474d4caa89b5c332e9fdbc0ed1be7336eceb893d1b4e8a4e8eae847314e31b298b65834765cecead87b71b67192b8967810cf8d5f1d7f3fc13e617ac22fe389e313c6d78c7f92e896383bf14c1223696ad2cea4b7c9a1c9cb93efa638a42852da53755227a536a4be4b0b4b5b95d63561cc8439132ea49ba48bd35b334819a919db33fa27864f5c3bb17b92d7a4b2493726db4f2e9e7c6e8ac994fc2987a7ea4ce54ddd9f49c84ccbdc99f98917cfabe3f56771b36ab3faf81cfe3afe734188608da04718285c257c9a1d98bd2afb594e60ceea9c1e51b0a852d42be688abc52f73a37237e5becb8bcfabcf1bcc4fcbdf53402ec82c3828d197e4494e4e339f563cad53ea2c2d93764df79fbe767a9f2c5ab65d8ec827cb5b0b0de086fda2c241f19de2615150514dd1fb19a933f617eb154b8a2fce749ab964e6d39288921f67e1b3f8b3da675bce5e30fbe11cf69c2d7391b95973dbe759cf2b9dd73d3f72fe8e05d405790b7e5de8ba70d5c2378bd216b5959a95ce2f7dfc5de4778d65da65b2b29bdf077cbf6931be58bcb86389c792f54bbe940bcacf57b85654567c5aca5f7afe07b71faa7e185c96bdac63b9f7f28d2b882b242b6eac0c5eb96395deaa92558f57c7ae6e5ec35a53bee6cddaa96bcf557a566e5a475da758d7551553d5bade66fd8af59faa45d5d76b426bf6d49ad62ea97db741b0e1cac6908d4d9bcc36556cfab859bcf9d696c82dcd757675955b895b8bb63ed996baedcc8fbe3f366c37d95eb1fd73bda4be6b47e28e930d3e0d0d3b4d772e6f441b158d3dbb26edbabc3b6c776b934bd3963dcc3d157bc15ec5dedf7fcafce9c6bee87dedfb7df737fd6cfb73ed01c681f266a47966735f8ba8a5ab35bdb5f3e0b883ed6d016d077e19fd4bfd21cb4335870d0f2f3f423d527a64f068c9d1fe63d263bdc7738e3f6e9fda7ef7c48413d74e8e3fd9712afad4d9d311a74f9c619f397a36f0eca173fee70e9ef73ddf72c1fb42f345af8b077ef5faf540877747f3259f4bad97fd2eb7758eed3c7225f8caf1ab61574f5fe35ebb703dee7ae78d941bb76e4ebad9754b70ebd9edfcdb2fef14dd19b83bff1ee15ef97dddfb950f4c1fd4fdcbf15f7bbabcbb0e3f0c7b78f151d2a3bb8ff98f9fff26ffed5377e913fa93caa7164f1b9eb93f3bd413d173f9f789bf773f973e1fe82dfb43ef8fda170e2f7efe33e4cf8b7d13faba5fca5e0ebe5afadaf875fd1bcf37edfd09fd0fde16bc1d7857fedef8fd8e0fbe1fce7c4cfbf87460c627d2a7aacf8e9fdbbe447fb93758303828e5c978aaad00061b9a0df70dafea01a0a703c0b80cf70f13d5e73c9520eab3a90a81ff84d56741957803d0046fcaed3ae718007b61b30b814712d8945bf5e410807a780c378dc8b33ddcd55c3478e221bc1f1c7c6d0600a90d80cfb2c1c1810d83839fe13e06bb0dc0b1e9eaf3a55288f06cb0394889ae1b09e6836fe4df597b805de7ee9c9f0000000970485973000016250000162501495224f00000020469545874584d4c3a636f6d2e61646f62652e786d7000000000003c783a786d706d65746120786d6c6e733a783d2261646f62653a6e733a6d6574612f2220783a786d70746b3d22584d5020436f726520352e342e30223e0a2020203c7264663a52444620786d6c6e733a7264663d22687474703a2f2f7777772e77332e6f72672f313939392f30322f32322d7264662d73796e7461782d6e7323223e0a2020202020203c7264663a4465736372697074696f6e207264663a61626f75743d22220a202020202020202020202020786d6c6e733a657869663d22687474703a2f2f6e732e61646f62652e636f6d2f657869662f312e302f220a202020202020202020202020786d6c6e733a746966663d22687474703a2f2f6e732e61646f62652e636f6d2f746966662f312e302f223e0a2020202020202020203c657869663a506978656c5944696d656e73696f6e3e3531343c2f657869663a506978656c5944696d656e73696f6e3e0a2020202020202020203c657869663a506978656c5844696d656e73696f6e3e3433343c2f657869663a506978656c5844696d656e73696f6e3e0a2020202020202020203c746966663a4f7269656e746174696f6e3e313c2f746966663a4f7269656e746174696f6e3e0a2020202020203c2f7264663a4465736372697074696f6e3e0a2020203c2f7264663a5244463e0a3c2f783a786d706d6574613e0adc0adfa8000040004944415478019cbdf9975dc775df7b7ac24080004180a448511249c99235d8966d65bdacf592b59265ffd779ebe5b767bf48721249912c5103e70100310f0da0877c3fdfefde75ea369a92e2eabee754edda73cd75eadcbbf5ebdffcea78d93a5e8e8f75d3dfb21c2fcbd696d2a4880f9060cef2dd70650d60c5c34544fe175fc375114ff3260aec9400ee966527937882734cde38cdcb3a8154a8a4c9b32d86152d602319c1a941642d5b5665816d5dc44b59e11bd88a019cfcd0c6872634bcf19acf69e9a6ed3ceecdb3ef336cc6fb6371680933ffd360338f39bfe39d9fda30fbae732443605597e7bcdb856f5a5cd44e1466d50c9753b2567d2d7b2a7bd30fdac8ed3201171b3bbd6af5c7636ddf5c365f166f4e737ecbedbce6b7a689510f97651bd3d09f44ecda56ba5d4ae32363f54f5ca1ab329ca76c3bd319182b647b3d98e1cb3569d04b9aef51c35849836654f88786dbf1f151f8926dc08aa75882e016ef4b039bf79c4e3cb80de73e841b38cbb75d6048467f409a1d1fdb632bbce77cf2fad370e3072d57f4970adb2e8df0eeece7703b63bab7be806659a4c96b1e8d27d0149240efc63dc9a34c9a682a3a32a8375350a22186b753d0456881e175c92e5cb4b09ead5ceb78c27f9dbddad4729ba0d3cfdfdbbe9339ed17e073bcf166d82a77aea7916d3cd5d7e3c383e5e0c9e365d7a3556a75f342827aa63802320cb27f6c19de60a4239f5c5cf57c0136b3129ba4e98bc43c424d66f02a2d9670c790964dc327173da24651b40a834303cc6140c36b430a621d36f31a7afa7dc6b50fa4103042a7d1d38e2e9833ebd2387defbc4eb7f63436bb56b7d6ba71bfecde7a9c963fe7b56ec18beeed5b6065ce6033db3c8015712382850ac58da5e12e287497f6cac71ef39d990beef2c55f8d37d313177eebfba7fc6091ae27c5e4cfb835ef3f0375e8b1892bcd514c8deae8480debe870397af66c79f6787f79f6e8e1f2ecdedd65d71e282f1859c66e0d8fc439669a4c7b05a77fb9e39168afc3c9b1a1d4e4603bd7685c4211bcb872ae14cc459ad47445614a5f22b325870f054ca8ab6f9b186d52f3063b7611db0ca71546ebd8b6741acaf0c1976b2569f87c274e9869ad651947e53bc92314ffb62bbc36f895f214f99f533f5b4ffcd17c28e594a22d4961dab991451934aeb56edbe05166cc1c00b5bf5bde067dd1ccb73f953fe3126ffe27e19dee7cea864de98c519f54328787cbe1c1b3e5e8e9538d58fbcbd3876a540f1f2c4f6edf591eddb8b91cdcbebdec3e7a442373ed1b37f3a2209a690b99a4b502a0b812142e701bab341ce05cdc577e0573ae4a95fcc80aa6c50816caa185215cc81f8c9ddd12227b20a281f23302029df8979ec16d0d5a560b5839756c2df086c01fb99d4e835af192d1e9c63aedde38eddbd60a5ccbe07e1ae1bf11863cf8b69c3fd5c05a3fc40d1ddb76ddbb14ad6529bad60dca79d51e7497bcdc93788c58750261aa8325b375b0b89054198bfb0cacbcbea51e44efe6d1797db78fcb270dc396233a78b487bf3e47079a023e7db21cee6ba452a37a72f7f6f2548d695f1feecbfd07cb9646b1ad274f9733c2df113335b254dca16319de70ee0eab8fecae51afdc44a2c46906acae8a9ba1335ef1b3b3cd231c576729cdff9837910f913e78ad528e983e318842439ab87d33eeb133f44d01acf15a7e446ce2813f170638276d1eb61569f0a1dce405deccabe51ad3ca102354212731ae4d1f8c786664e2a64d7123ab23a7c93e690bb88d77926e54023b1447805c9f386f52818c350cd50c5e956d1fac7566a2299e40120d17afd5075a735ee58567c38338fb6e902ad2f2811167ea77cc14506bab43462a8d4afbf7ee2dfb376f2ecfbef86239be7b6f39d474f0588d6d5b53c4b34792cb47817add76ecba3cdd8d29d3baa4624a8a915bc9e019948a55f93638a8c10fcab892d5386d2a3c5da0ca2c29c6273f0e2082a16b7a6d38e1b78e5026d5a598990eae70eb7b70da86be0345dec93474316f73946afcdcb96e86b6cbfc2acb766ea23915bf62233aae21f6c73f23af1434af3865836e93837861fa9f08ad57dfff04bab3db3e27ec5e49463782ed507c86b76db38385bada0859d1c3823c7d4e86933a468f889cf3da5f1b7a8ad90c5f7997d7748b9a891c69fa77a80673f0e489d754079afe3dbb777f797cebe6f2f8fa8de5e88ed6586a703bcf0e962d8d6a3b6a885e5d89f1f6b6360d6580b6e80453a444ecb602162e2095b97d63981c84e1c1afd8405819e16c70c020147fc7406f3879ddc21def4222e1b052a630d0298ee00e6df8b534e456af015eb3d13dac1b2f192d2e3cd7bc2eac1450d346eb86c12171e0c90bd75c078fa1c58ab3d2cd14a7c79b4fe70edae177f45b79cf781dff3fb90ffe220a5b7c1c4f22a7f323b37ca69bcb31682e01bbc4d95526662640c1d098b272d2e558e566c8691e0d7e49423b9b75d2741aea66275c3a1b3b6556d1615794922e1aa98ed4605853b151f1e4ee5d4dfdee2c4f6fdd5a9edcbcb11caa51ed686ab8abf5d7ae1ad5b6eedb6597b5c1980a8caa19c8c8296b955f1b1f8525b89d6b26e56860cd8518792e000c0bbe5d55307883afdb44078340cc6b6568a4b5c2a4008c5a1489a767870bb23a804d41afb2120b4471c9318e49bab23435a690e1cce1fc5597156f8e9d963f5c3210c3732415398d6eceffb2f84c67bf5ae7609f6c0827793cafd74a37f39de9d2b6ba802cd1badb22fc29a61bd691287468cd97bbffe02cbf0b4757fd811c02f884ae888b0db97398a575dd6cd8ea0af15ed98cb24462fb88fbb15a8077ff98fe69b38251eb68ffc9f2f4fedde5e1679f2d4f3eff7c3950a33abaff68d9d6d63b53c0ddc3238d505b79ac82ed8c56f0425e2d652c9acb5042564c83d5aebc62a2184766706d40807106706506946b0409360cb4e4f808064140baffa082038f07089b3d10f846883ee8652c2e2d4f1a0cc3820c6702a2c8ebca33ec755e70c90b9e4936e24d979cf5da85b442d658f8c5e11d5f73d7d81fcb036b96ddf26658739a61e035dfa669bcbe071edf358cfbcce7cbd233ed285ff96fcb0e0c0fe3c4fd66ef1e5ef92913c945f4c88f1ed01b0453d711488364bde00fc4e4ba2499db363929430a2f7680104121a53c80e57eacd1e79091eae9fe72f0f891362bee6bb4d2ba8ad18a4d0a8d52db9a121e6b6db568c38246c57c4fed4a5c75d9d1d6056a7888125ffe4bc7966be54a1f978991d035a1363ee022a5cd43ac95245dba0793247042df0bc54ae46279a0b42241a5c108d9817b417573cc595c700c72920f890d5512fa829a8be18e91573a83655e95a15bf36ab8cb75c800994f38a373e3c3e1641ad81cda46601db7935b58217f595ee3aef921681dfa5e6c9a9b64ad9e681c78ccf1e26458f3dfe4737aca9c27fdf1733f338daf442797ade5a9b49ceac6830e4e52def844170560433f700196db31a5a2c11b59e604a6f37d2dbbbb36b55ddcad0f02794e45c3d20e20cfaa9e3ea051a941ddd6f44f5340b6d58fb4c6dad19a6b4b5345b775d1b10b68855b19cb25e148e95c56c720c9744408dc09b95baf22055a0fa351bb9cd9569346e9a6171cac0e8e555e8c4d2258e49640c74217a5e0401eb0152f71e5984c1767b5c4d057260c148283ba6938c13179107cb509134e574473b0b0e20dc7081fd49b492b34f2be2c125f8457c7072eca28445f9958e9913ff2563c618f0a0a3e3a46afd5d2e6d3f7e6076ec3d67259750b07ae92c77fb3447cc7615669f350ad6c9f92e5204050565ec06716eddb3486cea47ca14c883b90a22065a69c19418c93033ef6b116626d45c37acaee9f1ad2935bda52d70ee0b33bb7976335b4ad478f972de5ef6aaab8a551894171e8241e8ee32ffd5903f8a246e5b9a32979f125f60140b5d09ba2d2403bec0e58792442c88655196a41e1576e44ba718482b6254cf802bb70615101b53b385605d5b0cd7bf14a4d2aa1d2c484d2cefc237ea0d888c2519ca475b08ee096134b8f766ee4b6ce9b5a745e712b7ea7e13c0f9bf9b71eab075af74046239031ad67db85ec8621a5f9ceb0193e6b020e9f91af68ebd27001064e1751f2a29be5150f51a38df0e123aebabb83133ce9c802af03f84e0a116a279c9d385176e19a7285e223530ba3e874077654d3363812bc850fae46af7d3d00bef18b5f2c4f3efc70d979fc78d9d1ba6b5bf02d3542ada4740f2ff44e508438707800240aaea21c6f033739954f4607e1cec9e661184e299e355d2c062299771741b66b8d4f450d776ebd7075a17486b351d51841f635e922b74da93082b46523b3c8cb50c831193f8062b48aaf921203670dadef738ccdcb5c4bef0df5ad50d3c02df14ddeab9493b1b921745ec348c3cd15686248befdd80485037ce86607449799df44926839c5388a938477a72dbf881a3edf11c89f6b44f97396670d1adebe313f744d39a536159fb2933274b04ee4b904ca21d1936bfbe1083a7d1075bce8791571362e048096ed723e5b5bbbde98d8daa2196d2fe71f3ef636fbd6b3a7cb4e2b0b4d8b870f4c140c4b34b0c2210ff9d6127f340edaf8bf0020965dd6cfca89093442040b969a2e628b922db19a790480d892433243906828178299590f4595d03fbc07eb4093766648206dc34d6d1d4acd123f942ea3a30d94044b6b6de02c7e82cef20d35b2e0d2bb758e05c9f0b5044e10a2a968abd455dfe4753af64a1f1bddf6ff319e628eae11c2d5a1e99d90b256d7bedce4157960098e5120f229346ea155c401dd1283b6f506b229133a959d89c9ed48712c2683bfd28887397fd03a8d22fc2baf3990e1b881d091023f4854f0d60b4ce25bda8dd8dadad13e441a971b18542241efeddddde5ec850bcbf98b9796fd9ddde5588d0c5af4e332362e4896ee2d0f1c8f58c8e9047404eb92e8c615c162cc5548beae51242720c32319c856665c950a60724e3943f03880c2222166856ec645e7782ba204b8293010c83530c4bab656653f10818a9953a592c8a0f634c30cd18382888c422d7ec983323a0b2cc476727172baf3c9ebf8e045c40242dff0c635cfc9094d6f9bbf84dfaa83b8c9cc62bfa19ff9e2107d363d610f580db261e0fa6a1d92e70a96ccd022c0f99b3698892e436770f01119451ffb4803c4d1755714aeded6870c705d89190d48e191e7b8113b9ef594f928d3eb2be46b6462eb7c57bb7bf623fc0483ab7731a51bb8c8b02f85b77dfedcb2f7d2e565fbcc99e560ff917c821712845d31f449bcf5b3cec0fc69fc4d3cd30c02f42b7e2bdbe821f2e6df02d5e4a9882411e2ff11075c035b60ba62642a48718718e54a01aa03a699165cd3cc2811e2fcce0c57634eac422b9c48328571cc9564cb2ef1d0761895d8b0e26079144e154c21cf4ee97850c10dedc466b55f799b72c210d8a02bfa81d70a4ef7c60534e3cdf1091dac918c2de52381e735ce402292427641a45456bbd6525a29bc1ca0fcaa23037bb0200eaa2e862b623d025d6d074721bea301cd1f352ea5d769217506795b1aadd460f4d9613ac85ebaf927df820c81b3e456edb0fc336797b32fbdb4ec9d3bbf1cead9978f5e28bfed6b3f5b77518f7a0ab1b9e11ee2b1ca40524ab6edc082be7219facd48c26b0c3d27834c41ad29420104e8b404d0d00c71bc009040e700ad707c697af026dac223177e56bc5b3069321ccc2431450758111f5511c4fa80c667040a4889268f27d6dc89512a4e238aaff24ea00f3630a0e0bb804877dc8da008c30d25c1d80c27e9e75cf328007cc3b300a7f06a9cbe0ffac26d3fba62ad265ae7c675b9768518860bb9edc48ce6070f0279be2963d060ee8a085f07e513f35591dc69508a731650b51f1650322a6df568a5b8d3c0f8e8cfbe1662fc0251e4b50473b73ec2d69471efc517fd7972f3bae41c926df950c567d6d0da398f7cd1c3d6b23adb54d11190d0da5263c497d2820ce73a923c684d105d359229e538177d246d804c52d9945ef149419a08cd0276cdc72dc1a7ff48ab6bb2e01b4d1758995db12951a2023029091b32851c9e1d2f60d02dcb7a1bb9882467759e33203a11da516b3e0e9c1d3ec7e35ce982ddfa741a3ffca930708538f384ce79f0301b2eab3ee43b08dc3cfade599dbf4166165c563dc379e5bdf2699b831f73ac0c428badf2caceca096f1282e7e6abd1d29894e61f3af8707a82cd0ae2e62b9eba3b381a59a0eb5f2132c175f9230774d8393fe42e0fed74ec5c7c61397ff5caf2f8c3dde5e0705d970d7f979ec53c1b1c1a1d90e750bc93884dc4bdd32824b22377a54163c891e178f38250c123d9fc70b39d11a5b0442c4f5a05653352b6dd8296c08c5f8a94f350b5661d500ed2d2d6b0be84ad38c612239bbfe9e0334c3449a647162c9aa2f6ad0d175af1b213ccb899c322f154b6769ce418cc3d952ff916e9cb69e9f82c389d3fee25c73a58aa042043bab67d0658ae2f61345fbf043c504ecd8f4fc059ed1f14ae5cd111bc3090d543a7b91c867d42ebb8ef2a7beffcc99801879bd989971ad68ed653dc91d53e592bc2aa4f3400a7612913f80aaaaa9872b5e3f01de9296febfcf9e5eccb57979d73e7fc5672d8ace568c6e8ab8f836e6d39323d7d7506d20420d3b8c8ef007da58ad8fa166cd867526d7cf86853d362045c4b69839b997986a385c3c01a9021f8d080781c5139ca575a89193a0c683ac446e0067da8c801b1f818315af411ada8221cf8344f25f04feb6b4794148b32cfc4b81a1716d00459f1a98084d3f0956a8d7581af10144519eedcd0642857e0356fce9a796dc485beda17b67fce159b4e0bb33d91c3142e98d0988efbb001187e9d3ef5ec0a113ee747836293427ce0393e06346f38cab710159e45206b8b0d0d9428455c1625cfe4e5c7ca06041be3efee2d3bdafcd8bd74510fa6b52e3bd6f67fe55b0fe2362af8660d9f82592eb64155ba377728d61089236ff004a375a7eef880f040ab2c41f50f8b080cdb912659fc61e5f69d16341c160af25241c10f89380a1774f3e362e5e014d5ac9533c3dd3c9c8ba410da018abb81156eb08da88b81e668273618fa1301c7cf017566d8896c1750f25b0626c499f618043071b674b47d6d9bee9535549133a2fbca0f7d9aeeb9f8503754abee915b620bdc38b9a377d4914c401580c5a6e095cad14109d2f8bc759aef50b8616db30bd81b1535bd9290494c8b1b7772573da49bfeb8126c8763d1098061e62998f50ac55a5e826b5db6adadfc33973465dcfd5c6f2df3e209feafbae80e2172b12bf24b4b009682fe099d8fcdf19fb5b02fa04a3eb03544aba461e9e764a870321832d1665a7612133789a59115d3bde902d2553cc286abb17defca068753833dd0f2e083eae2013a96c1cec98a3793ee1a9d160278f14463f8de0eeb0a034ad0108038083b54018919e07678e7ba908daf4c33090ff2d70a30f3acfc4637a395a6f9fef1fbac5fbce8adf4e788525da207764c748a762ae6625f20dc735ca97600cb76cccb43606db12b015ff3ae7bd30b587541fc142738cfc9d0b83c0daf0b68a3d369187c526f6243fba97858cf2e1f89e221f5f917bc95bfa3dd469e973948aee55b4640a5951356d1a60b5a22b8f1313824e30a7e7c5438e537fbc3584dc573b2d1326c5f9828df4ccc1e13c9932113ee2a79564384030721cae3dff214315f390cc74440e4c1e24440d9e11411c2c25745fcb4c4f9a517422cc3970d4e29a00d502a854056a148ca475d1f8c837c172c99e089001a82f31c130098f5113e305dacfb0975e2c9a0173bf3b5cb8ade2ca7cbf0c304db8c46a7d63f79d66203cdfa18b2e6356cbe139fd390a0c3b6a78092e5531658527cfa568eb1bf202adb1baf4bd0591b7e11836af891abb41c02bb9477ca20cfc8065bb48295823da97bd2d4ab1d6de19f7de9eab2abc676f8f8a150d8d10443d742dff0d79418fa0bd1fa800fad1522d175a25921374ccb0560172cd1750b9f7431a4d0a10b63c515ec56c1ad0ff984bac7912230a1e0e683a3aaa11855f112003e0e349d6136bfe8904b14051042080569624e950c72d10954e04efbca05992331225d898c31eb780277387cf5dee031f20a02cf8675bcef4d14f6b1bd6136165ba7d07e9bef64b7de9b70b8429fbb7d03b242cb6fba2fbbf32d4b8dcf7d3db2246e65bb7d2b119d36415f100f8b1858d02a14cac01078a5ac0ac16a53275c7ef0e60f1e260833a81b94ba4766f2c2a7e38173e561f4eee5cbd9cabf7d933656219ccc1e1ef99784408c64218a055539659850d00d7065f95eca965f845478f08a5d7aa09ec45a414857bd0f7d730cfd2a01c48d00a2826e422d05b8a752a5d1140e8aa8f61b8f565060d317f5cc239a0bcd25704280f05de9fa0ee19f085d51a874edd39324aea003b8a9a3f3ac0b8e8c7ceb30c5216d39838d22f821e6ae3ceda349117812e67b78a512261b2ef2a151c3911ee564a7028f533fd283ff98015f5506ad67b8b7de7db732272fa5233a38441d94367d5b194bda172005b2dad61c94a7ff869b9dd11d8b14a2412a3ef107d0391cebe8d5cec50b6a6497f4407b7739d4dbcc84e60d0fdb8d0df8a0d3c5c47827ed1322a0f8ab10eb869f56de2da7edac8d0f3bd306a0098c8a483014a0629c162c74ce281e2b2be83aa5a80870be2105360fcb54b660951baed5cb91e8027707905c5d2950b305c39f382115ab650706d1a63380c023a1f3926e7995b9719bf3da57dcfb333b3c66726d39a52f86e8bf79cd7c10161ef08c68fc74d29ec01a97dcd860547c7dcac75cc4d4d33f9e59d5b63ad48c601dda06eb6755cd35fa388dfaf89f446c0f0670854e94e9c12d6394dd7653273a8ecc8eb71ecd0adb1c8493d0bc62734f279dc7baec05d6655796adbd33cba2173691dfa17d3a81cadf2be7c666dd4980be61ed1be0cfe92b74db41a602d47576314c36d65c665eac85d90538288bc190dc9ab7138ac6f8a24e0fdb6aa60ca24ce705535a47492bfbe5464423708b4eb7141277602916a2a4319660a8018a0d59c90db8319b5fe7a502b48367e7cef139df7114585996326821dded3c5dd1cffe0331f65bd7f229f922e452b639aa38b4f9040296fe34c3891ec983371f1f57aa3555e347c4ac6072daa6f069ecd281a448b0c141b755c50926a05305dae4553428407ee386b5ed80377a60ff18a1c1b7307215a0132c7894114035323d273b77f5e565578dede0d17de1cd44a16bf5d1cb648027bc86c1d1fe98f24c136164275ff793bca01b8dcc0d0c24287c9d4500d19f401e49741f2a0333c9801440c0eae58605e029c00b242b6ec8742983b127f9a9746d8fa5281199666362d2d611ba21b062ce34656181e3285787d9b90d9bf5eb78df1be7cbeecdef64e722a356bba552ca0d6d80474764004f5e4b280bcd50962a73fe042bb0f0ea9129f77994b2edbac07fb6a775c69309c6549474e2e3216c61e416fc9491740738944f195036235434f88146a79617bd567dc20fb215a3b815c0b8360849b2796f2febb28b7a5e764bdf327508bc1ad3accad033325a5bb8473f5109bf759d75223efca7387f273504276bb2ce92c2d1395a2070301d79880f5e94064b786e50c91b57b179ae9259919233f25b6e336ee5a34d1b18bed16da210583021754741d2585d696d15851fda764cf4a73e6cc287fe7f66042de190c6115e4d1a59ce1d72c8eb029a2b6da9717a81228182f48d3b3c9346069f34a4c467f99ddf72a30d78cda7bd494e87aa0992131b2cdeba59777ccabfe45a1593ad3eb6cbada264282fdcd0788af3cc0add058387a26b2ee9410558992568e611f9b1a3e9a13be63c644f193ffa70d1c20ce65660b05a15b73f4d7f0216a221ba93eb5df8d601088ae54244011ff79a2c100383646b2d78647524596225e638c116eb62018d145603e48870abd223bc41435ef10dbf24ba22da69c940e22a0b964e073f784148c5480107b25616a7710e91f2783800383d74619e96db76a6e2cf1854c02a84a9f01a6306751cfcf513cc35dd927a07b05e0371ed086eec6e09eb1d1e1d828e9c86b49f9ef742ecc64d5418f28b28ce73d27e47efce9367c95eb90d41c257bcf4356f381ab4491139f11ff8e8dffcfa8ef6b35d9d26dfb32ebdfa72e6e59797edb367f5653a4fc876401efcf35675a9a3b569c34fe71f687cb0ca85d58c6f01c0265f8de9e28cda4e8b33ac91690d2f479a89b2c64865491187b34b25d35991a98121df8e556e639aad10c337c5e5a26a19e499db7409638fa2e491240c9d146fa7f4dd08139c74e7350fd2a716de29f0e677fadd0a9ec24bd85232fad6550e699927eff04627462aeee333ce3f251f3af208cdc3898d0b721a205c174478026d1ea6375ec9ab3c0ac1f21521db288507bd7da834a0d60538109767d40b4448c62918aa4737331cba18f994cb86aee21f9bd12b71b6f2cfbc7c65d9bd707139d0f77c1473eb28140967a38748e9e63868ab1f111b3b361507e61741d1d97f60366e7804c24806ad05025a8d13e7a4616270f2c07228d8502048ce0aacf0c4b3ca3d79289456402c8e574e17003082d38ee922d0fcf0da18a8a7c0cd06db31e80acf8470aac49f716bfc14d6f3045f067f1eb335e81cd2e18e9bba07f5e31b019aaf7597b3ba31418def868f9d0eeff671d3340f68fe7868ddcada2e1ceb25ca4ae37f978fd38a977fdd8309861db109422b8961a6efb5252826372e89e0465749b02ad08655782aeed0f99d86fd6995bff3cdac121178cc112bbdfab27be9f2b2e8d59745eb3274ce5bd2118b6ce49ab772fd542c8a3563e74307382646e18166b6ab6ef09a759d463224c1974b988c24107134613b342810b464170bb4d045193241541030ce9f9b4fe505c3d7ce7501376de557db1cd8ad93790f2811f1cdff6c8ee0566203f3ff2431e49d205ae16d0f1ee88a8854a5cbf1f8c585000f120ad946df1ca59ca10bbc0973a18dc2757ef28c54781d9fefab8e116bbe126ff840441615c4375d567b12eb74f06632e7541d010e5fc362a279d976f18fccf08a6f126f7ea30e947f1a2e4218db00f338911f58ec83664be72977348a9dbb7a7579fcd1fbda653c18acbe2c027b8a2b176952aab9ccf05711361af00eb66f7df2dd608d64e088c295daf766344c354a780901ee408acec9d244ee1b8c3198d010371037d04211fdec283bdb045038d3220a7bdcccaf64531b9c6e4f08abe53e471cc4c1e7df12992b7a3b378e859b05b831c4393a2be0c3a84a09a9f1a3aa462b4578fb771db5daa84dbfb49eb10b19e08567c70d90fcf83329f228c1f66bcaaf31a147e374885dd219692a4ffa599af18a674407c12030627761b852ceb2200986ae4404186cd06e4606772a4b98f1922701f8facc0a15aab18e7bc3c086af68a1d111ab3357b4957f4e5bf9fa06ab9667552cbbb5814e6123099f80875ec80be8f96b23574ecbdaf599c432bed8354aee08517e33a6d086c0c264bab07964aa33d6fb5892995f049ee4157b4a6012f879d8ed62c79052269504471432aa42408888c4ffcdd79589c52a89ac888bcc8ee74e7e2a45f0c0919614360f48a767540d6bd5a06fdda125de773ba0cc6a39a12ba01389736d6fe0df35284e12befa4b13b2769613bc50b70b07f5000412becd213ccb292410a160614ae56e88c9a32769b2361e221b28709c215a05f96ce55d08bab5af1a1299d11fd848b3957f49273fb4d3b875fb0ba3a3867deb542eed6be8aca62eed6b602be7a28d80890348ab5fa78cdac23757d88419774076a30835912908398a2b93bc8c4ec0c21c1afbc710e114d93a8005b05e854f620e853c1aa5f28c1f8592b00e2a6a9024c42caa606656648002e91cba805c9873c646bc15a3b227a3bff3afe9fa4e2ef14ed398fac12f0ab7bc993d303ea1c38cc467dcc0ccfd8411f6c8c40e1f08860e65add36d827da08b64109c67bd4c62d8ea3ee536fbc21f0889f8ea0bf910f2a9803d1b6405b75dce8b9d224211fe73311b60000db56f8a5ca0c08c7e223f3257ffc56fa5d39ea68c972f2d6774fae3f1679fe8547e6de58b470e5f8017dc2ebfe886978a47639cb0b375cb1dbbdb364c58e35e93a13ef4a331286e93e69ade828a33e23b9b26563eabdca22f1d5dd185627c848385000b31a7411798d94dd914bcf050d2344dab3b20c37401a5f38be3c934600ec4ce8566b6909bd7cac03ed1a59def573f465ac852063ef4c67d3c893830d2e47705402e614e271eb895dfc86ffec8c7c6b5d0a0481500873c5d409a8253a603887165570ac1fc8c5e85631e45dff248b6ed452dc81a2bf4e76ea595f5424f978b989ab2ef45e54e013dc92d258684b279d8ae74e7cd7e6c05c0db0828e27599de2fd356fe9646b52dfd880468b6cbbe20be52254fe909387ca04cc7b1a164411aa99be5d33470aef7c962df90558e4f05462338c12a2cc314f2849369438b6ce088d4060028562da671d6bb9c69fa6824f54364a714b10b4560196c7f94d1f0c0c07602e99361cd8357023cfc216986695cf0cac71946867e9dfea551c5286c9c793eaf472a1dce30ab886abf4eb48d6055408e2b4c040c395547ac1f19a982f11d8d2ab4215df5122fb3c3e0e8403ab4981ee0491f02358f665aa4020e1ab1715879595480513a2215c7545d11ad3b7f09409c279c2fd3a550477ea7b943cb32cee20ac011ab33dafcd83e7b6e39d28f4e58560c32223493a6e601640ead0b30fb214a5affc022b3698c2f1c58ab91a50a3b8566f478cab40e456190e203b739cd77915979e325636e136b2123032464108f22332bd71e2b68eb8d9f4247072545a66cb309abc22b26abac0dae23017d02df9ab45692d591143072d248b296eac6846ce92c9191b329bb39735ff54885711a420272f5072cf6cc7c569dc42536874ad7c8368bca63da8349f145f42611dea9d02b93d80676cb06175de660ed261e230ffdcb81c35f951643f36cdc6815bb2c6b64acb69a877d107dd0243606a7cb00d2c6457edbd62cb903833ef7f80df8f199bd654fcfcbce5ebea29f46ba23805ae1c035e1266f9c95fff19d1fe109b70a6db3929dc7bd9f9d19068e60bb59a60bd33e8e61b069a418d6d9c9372d4815ec16d35bb75c2aed460b73fe21b7712be12ab165749e9cd5960a29782936278ad0385228e228e4a2d7ad0bc8360cb65dc900249ee776704801617b6f52d80fcae1cea77935fc3419c98bcd511cdeb36249f631b0e6256805f418d111c1566ba90bd94ecb767049532e09d195f8e03df28092c0968a9bbed3c014b057b7b617d0c938bc8d43e650980421be053c44af0a067f4ac3c74104b39c0673475ee7711fb629afe18dbfa10e6b64b6f2af5d5b1e7dfcc1ba2e33f2cad33a940c7f6fbee2ad575c352c6931e15034d60182c92ee2bbf3683328c5cb4afa6277ad9e828919714f041e8827f57c589de1fc0d3d0519445d60412045168a9b771962c70a603b8440ee286cc3438f1ee0641de51497695b1df525439fb541ad304cb30c53ad17cbb746818147c8dd1a2bd515ac0b3072523106326409a85c7c1af4dcbd462b105bb7968d9e89c7573d4ac223fa36b7ca2f61d1563afa0ca1917541992814ec94031c9cd34e598592e5403e4e8b4c71d77f3815adb25b9f94ab70fde78c4dfef022b491ca8dc8f669f13e458f26735625b634653cab2923df627574a0af24b0b2dc6c1558014154d014497b29f986750108137bf2bdf97048688f9588acc9cc97fcf0f33d152206c108652ca029c11f842105e4201c46ad16062cf4e203bc7878640b45782b6e19be347b71a974a10aa9f46a3ed2a34723f4eee0829c1e0e668322d3bedea0683bbbf0a1ed38723b3fbce01dfeb34e8d93bc56169d565e56cb062b5f0e889aa964188daf80c11d0e91928865b9270bef99af3086be8e87d986dec009e6d33a4521a0a6771917cc7643207d23918482f29d06419ff6135903cf420a802c670ad7bc95808780b193dc7c870892cccf99f8c78a422d0cc5a774eb1a6a10e06949c62f1691d36cce6aca78e5cab277e1451db1d28ffef105abfa73cd2e7a44208a008f349ee8657d0cafba278486211f4ead81ebb8d361b69ef810066831c872362e36cc90995d746ae6ceae446e65a16505d275857bc7a1b35cddc1c25834c955f7a935c6b0f0a2036e437317177bca5c444fc8ce5f6f5498a770ba50d67be48666e5db65173cf84776f522437ee892e7ab0ba1b8610a818c226fccd88ab50d090ebe7118f88a541c3876b6ee417cfeda38f109f93028c6b37e02b7ffc1b14e864112a1e6a17852f05228c401035730240033bc68da9c95aca87473bd13dd33fdccd13376fff47746eb28caac790d3d56887d00bff6c36a67a4a11edced5bed326eebb59733fa1aeffd1bfa76611fb1221ffaba3b11b3062ff2c268c809f67a457ef0db3f125c7e5044dfbb584636c9ac30849d763eb85dc90458dd497c0da629c546866801851f1133f06da52c18b806ea9a88f1d74d0a2a7b3e460863f182e93afd8305f2f8644b3d3a98b53129801610e87038accc2f72a237bc82d7d7c02b6556e2e9dea3612900d3418b49f261a74763b21e81bbc08cab4bd1b4bcbeb7dcbe3f57562d5ebe691c4073bc79599ef4ea3cdfa15b119c17bd042d7f936db98dc71dc3c8b72f7463abaf9cc6dbd830c5e7a0f0b5004ff51b624ff4ab970f7480f7a67e5fec0bfd70df8b7a78fc97dff98eefad037768067f031008bc84291ef1f83cbb0dd842768e585d58ce5ed19451a7f28f1ff7ab2f2bad3533af0936f1b6b0e9d27291613d2da8e2135e7d83b098a288152a0168db9e2a82b5420400bb7618d611b708099b454dedd23850478a552bee0318678289d2460c36d7c092e7b514dff7c71ffaeabf75705af87d9f7dd5b00ca511dff9b9479e5d00d3d2961bb4c8400fe2e625b861d604747431d0cc4d03be796df28b74f88467464a1b92aca832e4052811a5f0e08d5e136c864333e7b5eee6655563d397f256065a4bc971b75a25cf74937c09eb81dea3117434b667fa21741ad5fe93fde5e18387cb2dfd22e6a79f7eba7cfac927cb8d1b379687faed669e617ef35bdf5ade78e38de5a20ef7dabf381ec9e233cb22af20c3be5629746db7f0342a2efa76e15ded32eee87eb8ff5899fa872744f92ff6e5cb82b5d808478d4d5f0b2d0156fa733b81a7959bdf8c2e4ead5c2320fc8f077182996f186dce2b599266110528d006a210e904570427c054a433946d9d45e6effa93c3e26001d0bbd909afe50f3b8a37375053d94230c71bade9bab01aeefb24a7e1034fba269b2bba9726d800c805131d5a46f3e01e3e6df0f3824ed2cce98ef7bd1b11e98eb7acd8dc2904270eee49fae771552ac5b33918075a1980d6747c04e08c54cf9eed2f8f7466f09e7e66f6d6ad2f345add7083ba7efdc6f2851ad9a3870f8d07ddae3efcfed803fda6f33d7dfbef57bef2159fefc481c33f1292b8c53c77699ddb160c747f87727a18bda35192efff58eedc62d1e53a91b289fe83ce72e49cba379cfb4919b0ee803b5d777547aed073accade2967a77e900829d7cec2ba1696fcca99a4206018650d3b333c4dc145bcb81df9e58208b1f2785024c871efa32c38582e04685df94a299ab49981b8114c20c8aa779c14a48ecf4e236ed9e6c565a585aa1d680e6e4874122d2718602590171d9b5d648a62c39785de5442c60d091d3187066edc4ff242066186b7dc4128b68dd7b8e077e8389c4ee281031c6c1fda55821d4a8e9d79faa7df667ea8c6734bbfdbfc9946aa8f3ff978b9ad0675f7ee3d8d600f96271acd2c4a74fc3cd219557e1a19537af8edeb813153c7b7df7e47ed622f7620541291897993aace99ed231e1c638f51756b7767d97d51df62a586b6ff99d67bbc2d2d14706350cacb40409cda419010665f5ae07c11ce2c1ffc66095a363ec467302117dd141c6d064a3429a692170cddc00f00608279988379174be779be2c400ab22ab5187894524f1625719485463704b40ca2f64cf128e614743b383ab5b3218ca34abb416f11baa04bfba0ef02187d38b064025c7d810ee8139d88175974c93264c062737007df7260d38943ab39e85af4d0ad304e4b5b3f11acfc8154056d27aa278cdef86695d734cdd739ad183c856d0ac1325269fab74fa37aa0467477b971f3a6a67f9f2e9f7df6e972470f7e596f3d3bc8cfd14247e8739d69a08186a7ae92f1f8f1fef28546bcc78f1e698677de34f61bd9fa8bb6abce46a80bfab70d8094b49d60fb37cf3405e507291eaaf11e6afa0a33f2c2b7eb009481616cfb0298f520af19371e990af1103cdbdf6e648848e504c50106468ac2c0189d1c946585587728c0ca8c3b1f88e264a731911e99ce33a12e28ea1ecc0b62338ef2f0852622ca20f007e5e0195de08f1ec24136f7426ed1ad4317c09c6eae2d109eb6490c6d0b0cc3ddd7d0b632ce747ecb6ad9e66b651c4b52489d3febe0b858c235f0266c392b3c398137af96d0e9e6dd703857b1460f7b29b9e0a23bb4a6434005600d67bd74a0dd3fd654f735a56374fae28b5bcbf5ebd797cfaf7fae91ebd6725f23d5c133dedb3af6d48f47258c56841449f440cea1f8c19bc6e6d75282a5c67bace9a2a68cf7ef2f2f69db1d7aaba44b553bd3811ef8a430308c71e0beca632bc4afbe70c48a1f0ad4884985593ddc3a423c43493f1f90a3858be5498aff80b5742888e7f7c948581f5df817ff88800062f253398ca734046d70188716064e83a8ffd5b1e252d30173245f995d8866094105c32b6ea8c84d22d878adc68a05097c8795c59aaeac00d6eb068d8c318b321e59094d2c003092fa700b4ed10579e31a9f016a1e1bd9f6133ae065fef00b772b62f9f10f54f04a102f470bcfc0ce23b1d2047fa235492a05856759904ee4ad0f30372a6dab3fd5f7c933aa3032b141e146f5398d4a6b2ac1f7f7f7352a68ea255bbcb6d2b42cd3bf340efff09ff4673ac90c6c5bbfe9bca3b796cfe8eb01cee9e130a63dd01a2c8d33fad2586fddbab9bcfebad665da0d24b41751d77a964f3adef7f655570913eb62333582edf16b9c3a01f2ecf6ade1577883df6e86d748980118e6306848816f5ce792f0bf52e02b08a19e9301d04738a963a45d04beeba26c0a45414430b62121312c5934c4e4e561afda399d9880fce59f4a009ff0b3824e934da442b48f1655f1c906dcc128244c96ca43f6061fe5b56c505300e810594e9b6798a7e2992838ca03b7e546ad35bdc1c71e8277f44c1efe8a2f91d8f8e812bd75477e14d73df438ce74a28566a6b3cea53f6ca2d3c4a2688c0782027178cc30f345895298e9df811a158de6716d56dcfce286a67e9f2d9f7fae8d8a2f6e7a5a089e06a1c8156fd6527b6a30ebf42f361f1e66a4627ac8daeaecb9b3cb45fde2ca15bd7672f59557966baf5c5b2ebc7041bc3f5bfee5c73ff5f63d0d92cd753644beb879cb53d133fae108ab8821e5a8b6039f9e0cb3afcab442915e5a8eec4a87b37a91735fafbe1cf50f529c64a2b48b05bf398f54c2cc1f27e8fff9508a9135d6641b584280103c9030a88d02b01157be0b4b95825eca65566ae1990d85940d47d42e1d0cb110c56679edd4d6c3884e080ba5743397ba5b47382313e4811389aebce4d0b88ba97503af15084700c5a39818920b34d0cf76256d46e214fb5612b401dfb70287efea05c5acb4b2c7bc3ca89153bc75233dcb06abd3cd0298fd819d15fc037cb16aca3b56a33a7445beafa9d9ddbb773c05bca14d87cf19a9d4d3b32bf85453c48c46e517e9c048655d9abfef5d4fd4e8f43b612f6817efc54b2f2e57aebcb45cd5b9c1abfab2d19735fdbb74e9a5e5c2455574359ec3a343ed2a6e2def6aad744b534f9fb2d033aefd274fbd514263bfa48d8a35c44a64bb1cd78c8d747cd2fa80b496d9d685179673afbeb23cf8fdd934b28db22986f88e676db8d022e3cb7669fb9c4adf4536eb33b3d48f00460173abd1c5c8ca20cbb272a9c2147915b42b8915d82c7865b766e221e3aa70a371984665239611932cc88b66186310f8a26c078fe42c1fa03ece8bf4c45b7a24c3ae430a8c147ca2c79a477aa53935de34f63682ada27d173e2024365f4fca2a6da1f6277e84223c61827c52cd2eba4f15a87c63bc62109ab552927eaa1d401efc7eac67543cabfafcb3cfbd71c1ae2023190dcb155e95688777b2f89e7cfd41db0f7a399a649f09eee99fa6758c4c34aa573452bdf6daabcbcb7a8feb457d27fd05460fe5ef6a2ad947da6ce5e196f22e2e975fd2dbcb1af17896065b4e7edcbd7377a1f1c38bbcd8843fd632c10fe633db3ae2b3a7a0015983815e7939a3cd8fbd8b979603d96b7b365111e150aca4f3ea3f32daa7611adcbeae7536e555d3c5249883331570cfa5a17e47bd113d527a2da67ef45ee9c1c23045dd8d284eb0c9b19c0a21e5a87b92e08ad1b670c73b86860d89d0e906af0eabd2404a5a5b2f4850bba2457e3b613867e21d414db7de9b65ee913f8e51594ecb808638fa2418db3274c97f154ce10acdd983089f8b419c33f889ab2b0d5c577f4606d7614f81663f01ea74fbacd37d473cf1bb5a5ffdf8c73f5e7ef2d39f6a0770fd0d2f7880b3b7a7aaa188f9608f689a07157e57f5e29ca67fe7f5f3448c34d7b499f0ea6baf2dafbef2eaf2927ee9925d41d65ce0c2a3f5813f818aed262a5917b5ad7e4d0de98cce17f208e0785b020f0f7c0a84d195064743ef109d529fc24b5e6373c565228615c01ba76b1aa83bdf62b5c3afbee8b37fe3f3e598dd4fc19b3274f00762e32bb7d24a111a23947048987d854edef80078a461fb99e6a76cc53245d084403b3b573da4e3a8763855a01dd64a444172ac922eabc00183ae94f0bd12867686e9275a4a7b0e544ac204266a8ace334234231a55e4ec7216ba477fa8f8accc5c89940ca4f3fa0eb7cdd0f81b6c263de26c74100f35263aa8112c67ad28489d254113f381125acf4e17d4156913969c359f9875f05d9c44c31a8a75121598291b9d2912c80b6e7941ac89ed68b3824d8a4b1a9568442f69f380e9dfd5abd7345a5d592e722ef0ac7eb2487874cad13fbc88d3811356ff53f23aa42bbf9cd5c87255231efc1fdc7f0096728ef410fbe172473b98d44b4641d37311bfb618dd7c6a03b842cb4d9cab3188544cb4ac6bd409b001c2dbd2c71a3dc7ab2d71baf1c5adeeb9d92f2e4bd1970ea9d5e209bc02f1d6037b77c9229f0fa3d8136dcf3e5643dbd24fcebcf8e24bd62c0fe588a2f049d1056d214131ae5db1ca2e5a1a40f18146d1912ea3506c84a61fb02a382140976cae96a69ba003375c2ae739f89051a3897581b66d1908805a9109a8689cd930f4e64303e9ca1addaca8e4d8e4c9bc3240f843cb21de22d1cd96ea5a76b579ad53e0c16bf943a346462b318cbe5bcb3955322a36d33ba6641d90e9998b460e1a0deb2a1a160deab5573452bdfaea7259df9971fe05fdd09e1a07235e8f56f0eed910fcfaac62f48b5f805b6f25ed0695173fd9c4687859230b8f045a4746581e13307dbd78b18f58e15d33c121edbed008dc7ee78ee9d8c3875037c5348430d26af4ddd3faec4875bea7c0e0b93e5a395f00ad61f267e7da9ec220de69ee6c0679ba88703ed0efb09053842d59769afcae110ccc3c6c9b09a95571702680e276ae10c0b7f1c2360cb482d920709ad33412a414cca858479a69661e936ed653bc7db73ad0ac69252aa02c4179956d1d928ca39d9fc6d27a1b5417c46034375f8b65e085441efa39af6572a7303a5d05a364f384c471771af0e2e1af9e53691a45d9c093e958a6f2e66611830e8a138a581a7cf5b7a78acd41dc175fbce8675b48c0674ccb18a9bef6e69bcbeb5ffdaa1ad52b5a6369a45263441ed3446fa2204801193d4a9146af7c94a72d7bb6e599023eae1d4bead48bdae480a73b6fe9c24362ce29b2dbf8fefb1fb8de3193627dc66383fb1add1831ed478428947451a736d8d6d22918cf5fe30f51ea7f5b9b2e7b7c559c1aefd3dbfc803bf8196d574a782b437ca16df9236e9a153bfce3830115ad1a19c47c94a9619e052c3d1343341f0a76cf051db3205e634aa48605d8718395d03f6ab67233a5d776e041e300577093b2214a9b6558393fb9c5a990e1b1ca58552206bbac7da08cb093f8c08d39e408d57a449999375c12f059290b5fe22e88ce8d6cf81417db3a689005befeac95e8c12b2ae332c53c92ffe9c97930cb46c09dbbb7b55679e86df06fe9202d2340ec11b155805b24a2c9a81024085635bb8317b5c3f7b246a8cf3fd7ab1f48d67496b3835f79ed2bcbfff5efffbd0fe932a504064f78f58786852dfed031573eb3a1671c02d601dc077ed675dbcfd578b6c6460b75eceffef66f97bfffd18f3205d4da0c1ee7355564b4e415977e5e86ae3c2fe3d1c19b6af46735b2dad698615b3015dd7dc79fa513a612940c0d118250ed637d8bd5ae1af69e763ab7773ed5260fef97b1aecb7b6641e69a7219fc4706b683be364c649f1676ad143942a017dbdda5b76280d3711837328d66528cd03c9c9cf9755c776637e4c3d7c62b4ec108e27f2f4495ea7493929d9088e9cb69c0db00f32a4582696d8ab6e37db794ba148a99adf9818a934795a144154cd92224e4da57a0da4280450dac46258394b6f5744e36acf92a175f54d266805bb470036677c15e91fbf7ef2dbffef56f965ffef257cb4d1d597aacca8b8cbff8e6b7966bda16672dc46866a6cd7ef0af081a434428df81cfe60423ca9e2af613ed3682e446a272c756462eee3c44663a957284451a2969d6f27eb6a6e3539cac677a775ddbffec58de54e3407f3a058e5ec1ff059e8b7df5ba9fc3d168e20b8dacd2e1b2be838369e1a347fb52148575c4ead163375276438d8f2d820f739cea744131bba2d88dbe03a038d81e502ebe30bec54a3d8370082b6ec8222f79b982c1c7c1489896fad1f7ce26ede92224da33f41c9a85eb1ebf4eb83c5223d3a2585393956391a2b9801eee250d460eba550c7f1a2769e15aaba19af2c859d3cda374867823247f35844c9c871c3b0d3d4c119c418c082381a08f7a016de21a39f83002a7f91594de82c04d7252508614cc6032eb63c4f0953cf0c921f47c7fdd99adb58b379b349d5205654a45e7c65637a3077cf97ba6e916e701fff0de1f5c3169506c42b039c58746012cfe46277cc2f3afbc00c9d9413ee030eaa5e144f7b31a3dd8c0a0b171548a3e5cad66b92bbe34684ec1b3e1d065135bf472a52a24e70bd9eef779458d529ff018408d8bd196753d2f60a21bb4e8847c3e0468eea9f15dd233341fa9126c67676fb9a435d9cb5a2731e279a4c47ef9859dd0873a5942034c2157196374b9dd8c75b13c3c577ee09e8037aba3108cf1675bdf2a9c6fb13aabaff0d60fb83b88b7fe0834afb0e79a308b9bfdd2f9abbc86f4c368a5d9a1e9e9427e3b387362e6c53e78cb8e4c07e3c620834a87564e8c646c1b2c0c9372691e10c880a2338f718961b66e031f049876450a81443914556a0f10782b2f22125937178007a19dd2f45d3866aa86e67c2b8a206185a1b38923ff34671b4117e8b7996e09974ac7629ed1885d34d61b9cf7a3423f50857de38daf2e7ff3d77fad69d355f3c5001a026b98b33a6bf7e4e9335554f45017a887b49c6aa771d228a35d3d5f5225f6b32f9d27a4c2b2a6e159d3dfffdddf49c6eb6865fe3438361b68d8b76fdfb14f8ea42fd3537483378d8c72c53fe87f5b0fa8df7befbde5830f3ef434f316a7403452f9b10f46c25d3ce88069548e170ca18c7c6cb4b06bf83aafb2d0a1c8919c4fbca0cd144656ea1f1d0f4e3b3a06ffde725b7ebaa675d92e3bdd159006ff6ec891deb9ebbdcba77dd4fde796d697672ebfe475d933d99bce30b6a213c13453a1771d6fd9e0347fe3a7784cdef08c6495a16e45ced14826e13c34a417a5276923c244c892d072ad849dab58199c4c464653184e2ca15d41a6110c16a9425775836057c50b0bcc0b7ecb0e565dd1a9018a406bbda1727ce5eebe4ac0990ffc4f8676922557c940831c633f47528db21841cf4605959629d31df5f21c21e2843a8d8b91e8e1c3c719695479e0fdad6f7ed36b1324a01295fcb27e9584350b3dba666e826fab011cb8a151460e3652dbde8f1e2cbffad52f97ffa6676037d5c09ed130c59b75cebd6ff18ed66b5a77678b9d0acb74d3238a2ab9470f2931460f357c1aa01b8b84d0406ee8d5fd9ffce427cb1ffef09e474bbcc12e221dc9491f9e4ca3270fb839ef785be71ee970d8487190fe6799bebe9c9195d76108f8844e823393dff8c65bcb9e763bf1abcb4034dc7194cbca3ea02334949ccdb821557ee072c652eb527e5e69ff537dbbf0c1babe32ad2eae776217fed052c682af228a6b6e889ee50375236b6770c7a13bea5dd87dc2091c0ee5b434888da7582a19268a6616484ecbefcabc59174915c6146d5878852f0aa2344639ee8bcd4485618cf5322f240e4ea52f4e17d019a9b8289c7200b819dc9bcd602b802cec2abd2dbc90741bfa399b0b98991272eeef5f7efa2fcbbbeffed68b78d6164ce558e7980efff1273e8f752afcc1c3fb1a31e46f37041590cae1b276fb1889a89c7e56a5691f9b135fd5ee1feb94944b74a301f2dc0bdc1ef9182568e83df29ddfcdeb23db5a835fbc7051479d5ef6563cd34a746754baa387c07402acfb32cd43479dfbab0e18996cdf47767ce1ca15c76e3a15aec0f5e16b00589f71840a1d794480ed7c3866c509914b7a44c0460f34f889a355744a1c527e410d11dfe2b3b83b76c300fc6852f22417fd0c5746df510e79eeb0c4efbc6c7ca0519d575fa007afc3a059419ed9753ef7c6a7eee0a30ea1ad2dfc06726798df510bdfe58b4c1e6fa9075423d357681d7b9d16e55050b118e9ab55134c9a90b9da8d06d953808480238adeb7400193e9540a8e54f113411b52e8e6d3f16c3a84af0b2002ca61c21a82ccb1c4a462b7239a579053586333c36beb88989b00004000494441543972a029bbcbc6d0010bc057c9f3668670a9b89f69adf2d1c71fbb00f24c4995d5cff7412ce5e400a6514cdb68844c01d16d4785c6aedb0f7ff837cb3bdf7c677989758b1a05eb2b1a5c1ac0ea8e331af958673142f1ba085a334265974e15556ba9bca34587bab5bca0918a75108770d3c8a87c475e6f314d638a3874d15a9075d115c93fb3f7913b60f83b50548ae0079795ece2cffe05013b5dc86ceb732ef18ea7cc991a5331b54120a7bca0e75617f558811d4d464e68dce8358ab35b99ad7f8dc4fab364f1c5e796899c3f12d200d0af5481c1193debbb724d3f48a11f0aac75d9a87f42c406748b30a2c427bb040a7ef3459b4d7dbcbbd80a828c22bc8a408f454fc7fb434c495e7821c448b3bfcca984d97f5c2c1f1455b2c647a23ea52b6441c039c1b15c23849f9d001682045f59914ea001580f65f62388352fb01e7d52e151ae89a18dacf0073f6aaeaa96762587ad740a9d4a47607328151cbe10972dce8d6dc8a04233dda31213401df7905817461bdcf154d3bbc8802f085bcb8baad8dff9ce774cc7c8c6e6148172a301f1413e3b6667a4d74b3ae5cee8f7f1c79fb821219b11800d13d6d7aef85204fd988ed260c9a7910b6a5e3446a6b7ec3a824370a3d4c84345673dc70608e6a065bcd5d70256f9c60ac1248f38f63152315a7ef580d1389b2bd43d1e8ed391d01981878e6cbeddd3faf38ed69a5f79fd75cbb6bbb141fc08e0753c90f54a5ed7f132cf99c055d1976dca47363dbb7d538f4c32fd5e79a171f8db6f1383b6abf9a70a5097e25bf0f9acd34551984808bb6a5c4c17b777b4bba3211467528ebde80eb1257341875c898a29aeb40140aba2cc489633c983a24374801f0aea5e598e5b4361da8850243b85bb6ec39b8b10e08c0ee0ea222623878860761c594ee19cc49147e5a577a707bd779751e69677cf6838ef7cf39b3e00ebb588689b1e7ea4e2a3638f36bc78484562b3c22700d46353493975c19ae7b22a39af7f3035fbdad7f23c484ca41d5ed05d95efacf03b8c86e5bc4cf1c94326a31023dc57b58942e5646383531aafbdfa9af8bfe21d3c9564b3f246c2256dac70348a750f8175139b339cc267578f46ee20e79cd7ef307352841107bf10cafb89a330be56882fd0bffd1c38fcd9e8610a48a792461cdf337de4c43ef632c5ed919aa9259b31ec787bf7d5ac901c9e9647aa0a10d9a9835645f1e8e34d3c121d34626e6be43cab4d95871fbcefaf2448ae0d1116a994a74994749dea1cc9495987ff6c6bcb4f97686a2e614c8fe8b9b77a3e9cc042f8f0e840bd0b051da121c110414c564a29ed1144c9d8023ea1ef49812dfd1c669bfd4e124629070a5335821d1763c21c217c84a81bfcaccf244ba091226e3c60a2c109e93822cf428d040ddbe74f97f7b593f6b39ffd7cf9e8a38fddfb5271dfd45a88d312d7aee9ebc5d4e342671586414a33ed16f405ed9881c79aeaae5e4ca4423312d0a0807b0d225e34361a9e0fc28a191aa15f7cabd10ade8caae2ebf251470888f5323b71ac5bf85a352a22cfaa2ee8e0ee3ffec33f782a48a3dbd3f3cfb993b42bd05b32d8f8409fdf68dd480366238372674dc67b5d54a2aeec5470d64c740c6cd9774382dfc9804f085d09b775f0d75336c9a501a127dbfd9c3ae9c00c818e81692c2317765327d808019f1d4fafe3e27193d9f723164e5dc19b2f77fc2556c4449d0e0c36db3ae8cc3946be2aeee8c9e3ca13f208c22e63429e86d532da078d33c82ab236325123d8052b6cafcbd4e3d293b3583fd43ae1b87782662ed61cb51588a335a16e2c6702078895dcb8acb2c8e910d3ac89b03b955c5231a8980f21704e834f4cdc25c3d4d4522aa745860e350f399ea4e90885b7af0aca08c00b853c2c453ab59b0d089ee9bcffde07eae56f2e5b1ad9d979c5273c2362ad40c3882de8680dcd8bf7b4e875c1a362fee0fbdff7c8f5b276cf38fbc79a89cacfd48f8a87dfb1a1dfdb6ad35c90ca43737a61a6ad0787f96a351a166bb8cfb5b9f2a976c718896864e07cef7bdf5bbeab0f5347fcefa2817f2292d730ceca660ac8ec655fb611686cf7efe77b3bd848a13381963b6ba62b57af2cdbbfd3264b4d9fa1e9ba43dcbec01d563c693a5ff8620d3e6424bb7fffbe3b1a771ed289cd14a6afac2b790ce1131592c93a9546863fc93347f8e373176e351c9240879da4ec02df5b21672bcb5f7ba0c6c50ee319c93db87f57b4d965b4ef43adabcaa778222f5c4bf448ad33049767e1bb9141bc3214a14632e6efccffc559239976c4b42e3bd6d4dcf6b46033411cdada62476db7717431b8542a14b20c696561daf4213075745a89922e63c12fb64656c24e00acde3e8ee00e6bbe9be2c0bb6d4fd4837aea71ef8e9f4ff11e156fe0d29bfeeddffd70f9f65f7cc70763699854ba0b6a0c340876e6904f8fceaeebdd3bf7dc1b5fd0b4e948158d06456744c567bdc1f329befe8c03af6c46fce0fb3f58bea92926af87a84d990ffcd83da3a26225e630da64c4c11e1a547853f9791ec586018de953352ceea9788fd509eafdaf5a6fd141d2d818d96864c8c1c5ad7f2a8b1a5949a513b82c3c1e06efebd91a6541c3471edf1ac59a9c9d3f1a398c78ffeb154d3dd9d97cf8f0916d8167f81ac54562fe22011e1d5246c0b764172325fabff9e69bcb9e1e1dc19ee937cf0519f97ff3eb5fdbaf12b01c686465db9f23563c8660c46b793123d6e0c596977ab15ed141da04a0064d532175cc216735305e7dd9faf4637d573e6bc1c2541940074ff38580cce255edd138335e84e4ba8e6405450d845399fca29eee54203640244599f4ba4415b7c0c4134599d0c7d0e0d81ceb05f72908993e63030a23318e4382dbee4b4a57082cdfc5150339b3521d892ba72a1d8de191b6c5efabb2ddbd735b8dea0b8f483c40bdadf443e51de8d908f6beaa42bda383a23d3a219f8e862dee0b5a93d8c9d402057ad5077a53e1961a120dcba71e541979a8cc770bf2dd172cd499dae0c7afe87b2ade79e71daf71fb1915fce8d531663c63127b7843c7d93ff4e6a1adbfac460deac6f51bfe42d047da05e3f91753c52e04a678348c1d8db6c8a42341af37dfe471402a24f2284b683ccd2dc7b3c14527f3b25eb6e43958c2b175a01130b5bbc0f329ca4599d95cc90e268d0caface595d21a652a3b89e3e339804f87444781bd8c60a9333c80d756be46caf392f9543630a3809ca92b0d2dbbaf59a3ae725729fdea4bf33b29d7f0d2077a0615be8b71570fa6b7346a6ea9eec4a85082dfc1b656dafe107de7f75d208595c68d6c55547908d587826224e38e51ac4ffcbcacd602166a6ef406d528c49754070b851f6ccb288447c7c2830705615ead5a1a2b29a635b00c8d70f3af7b1a3b73762acfa1a650f4b84c43ee3fc8b1a04f3ffdcce7de580bd12b3fd439381a05238f6d64d12b7b62e3a1f3530911c93aa44f875f5d3ef9386fee5ebe9cd3e2aca77ef7fbdf2f1f7ef4a11b159b003cff8177427ce8f58de0f7d56b53a9fc5cab8cc027f8f4408f48fcb56a8c1c376fe888d227d69ba920d3a947d29b1198510d1ab6b719add9a0c23704aaf2ec63687a3a4667f1407ce0879e9cb2fffa37beeee92a65042f7617fdcd50c26569804ff06b367dee6a6326af3d81df5346a66d4ca33b90873a943731f3264d1dd0c779158786b2fa421d1f1b42639d251e3c24e76d6abeba808e023eaccb78664b07c679c6733a01b319528f2428f245d38168d7af382c75d6f99ab110fa575fb6f4fa8e1413a0e9cb1a31410f87baad3cc5b5f2251e6ea8314246b2262af780802373f28387d29a0aa907f4738b3eee681693b2c5d2ce14bf1826a0e2f08b8864b4225d08286503504cc6416b7a780ad9e010099e29140d3f23957a7c55604e375cd7698a4f99fe6934a192b1ee620a454344aff4f4eb16b8a795169113143c576204a0422195170ad914f8c10fbee787bf172fe6d57a76d6d875fbc98f7fb2bcf7defb3ef7a72665199e62a3f1e400beaf82ed707462516f5364236b1da663bffad77f5d7efbdbdf2dd7358a50a9d9c840ef04f44617769be910d84e817d3a97b68d46019cc0b48e976f7fade916dbe434543a1c7648d9c9fbe1dffe707945afb0b026ec704ebb86ec30d2d8f0834759a9002d535f4fe97876a7b9073d3f5346364bde97fd8c44ae7018d60527daf83c775d93877ec2e1c326126b2c3a00762cb10138758f2923cfd0f87254d6b7941df8741c4c852f5346435eea083e1a5e539ef36de0d44094864cfd87f389bb8eca2f7bb2674f7a1c6806a16d46eb4236ba379e8061c05d017d3bc4de35ddf96964e11405a5a9ff6430d3450a966726de61544fda3260e8389c4ad1164643299656ce62adcc6a6c140a0f414d4ae78131e4b1ac2290e37589a647bc93f4400f48e9dd7036d33f7ac29b6a54c0d81123989e4ae78f1eac9b9f194b671d12c5c3e21c5bb0765de873a6900a46ef0f025bc95ffffad7f4f9bacfd751d8d0f25c8b518dca895c2a888314867fec03c281da67aaac0f3cdd81f7964622e7883fdf07fff39fff428decb7d60d3e3ea604bf1aad15313e370ad50d4b727635c5e26ce33955102a2215968e10ef3f56437df777bf5b7efdeebb594b8903a301f6df93af38e6e50d04588b171b38a4a9dc34321a2a5f0340a7c3949151c75be7d6458f262493ce08f974c0d88b6edc61983fd4169c8e4ebbd30478d0d0d94de579d8d7bef635aff7c8133985e78e842fda7945ef979d3b7b7e7970c00e676ca7f3a0ecdfd00e6fb6fea18cc4e890745fe746d03074f20c093a646293ea395f49705e8f3a9ee87b4f8e9ef2203c7c8d81597c2ac4ce4e59edce99fc10501ad9c8165331e203477a4d5736a5982e327a6c06344413087070e5a23909c16304c9c4834305ef02a1f04347c1d0811f494e46aa3ca3c2a9bcf3c46974d63c9942e9d850f5f8ed4816fcf482e911c5d36aa5f0c777354a183691c548827ed053f91f697dc1c9084e9073decf410ad3a0ec5474732566178c57332e7b47f2aea661ca320d9d12b86c66c013dec43ded52e5a537663a041c3da9d4573482f0750034001a183acd015c78a2330b7e76dfae5dbbea9e9e171dcf698af3c1071f2cfff37ffecc8d82f6090b6f5428821c4f31e1a10fd3562a2b7ae1331cc5231bde80667b9e512fb3169db66093478d92064cc320a0096f4d5fd2b413182311f2e01d5f52be7464aa43b295d75858d3bdc417ec68b4785515998d0d1e98f797ecd86e717155101f6ca2d1f30804d9a91b9ccd7ce48e89b2a791c53712451d34b1e215c8ebd0bacd77f2e09b7fe9aa06cdb70b6fcb36de961eec6c579de037d1cab7f9afa2e6bcc4ddc81841d2b0428213d9e1f29451958942a291b1d8c679e9cdc14da1c5a149e3a6b61783465094c6c436ae31bb40bc255d6fcfaaf0a98c4c07983ad1b0d84c60670fa7da69e691c2408faed4f0c424178647128d08faa312b10e62c4618a7349df8af4a27a49cec1bda7b770995a622f958aef60e79b7199aeb19b86fae4e1401a8c25204419f0a51270aa9d531154164e89d3bbef6be4ffad46118e53612db4d8d55f0a833ef0a262795aa4290a0d996fce3de933216a62400169f4d4cc822ffbfc915e78e42031cf9268ec8c94e8ca94934a681a74542749e7426363f380914c6f7f49977cfd1b744c6ff119f2193dd01fd8537a72690f0e27e069684c0fa91304377675102f5ebcb8683fd28f0dd8c0819632e1a1355ffd4663fa8abe60e7357d38fc4ba3e4040a78f0a20c2957976dccb42d3db2d291f9d517d53b8ac0cfd7a40f0d0fdfc55f22c44ecac6e5459a924be8c676b281e133996efb3d3b3823ddb5cbbaa7b23c54797547a11a607ee6262183afe0ab940d94b229f26bba2832346c6add51a81b194ecbc98f54f4b922b4616117a561630350ce20a922279162ad4183c5598c1c14de175acc7afaa75d3f16b68c54f4b607ec2ab94714a118d2d83d952a01eda48c2eadf3ae4eac9ff7b63beb291efc32dfa717e5a5c04b9ae261cfc73a4bd8a70e52c03923c70f22303da600e30f694dc7a08fe511b53adb7ebec3eb23dffdcbbff41a81e353f4ae9febfd2ab6f1e92468605b9a76e598526ffb3322b8e8dc01a0270d8f351bbec5bcf69ca2d53b11d108c2b129553cd62bbb6a603420fc92674b97bc3b4865d4d86cd93082270d8c005f74614ac85a8a0d0497a1f259a331bab0bb4719d1f8f02d534bf0d910e946b6ab064583611dc71d1ace34f2703d5fb043fcaa7ccea97abe0b440fc3d9b0b116dd6971ef2508fe5d3f32cd1d177621f3e840768a9aed75ea0cdbff34e074b2619aba78a2a352167ce78616ec15e634e52a393bda65dd93ce4ff428e3d8f52fd87d9d4b2771d14970cbe08e975b1e746e6438b3ad6f2614869f97f10c8146e6e9229b1fd3b0292268610b6f46a93ce44408ec6320ec11c0f48ce9deefd5cb7ff0e187de8e65947aa0c6f6443de681b64ea3b01a93863da6555b5beca0a15f2aa595b730ea9e7a4e5e23d75487dd291ef252c06cc773e89534a3178d8e290b5317f8b203481e05c8c36076ad7818cdda8a918c5ef2f048dfff20bb6d47d92289b283447a301e5cbffdf63b524d955815c8531e6573aa831d3c8f08350233225051a9b054c414069b199aa6490ff4a1f2e3a7c8c8153cfef033be8707bb845907d6036235324e607070d80fb3858f6c1a3c0d906784349096c9ac80d902a31e233032910a3e23078d822960b9d91b3bfc94110fee79e6071c9a4bea54bea787ec9c27c406a6bd744ef637f546be453e3e6cfd1599422a277a51ae7c78d0ce8c893712789c808e7424ccb650923a4403e3910c4b8a1dc94921b5154852406814b5bc96d13ed84489fd1ace976d465a1ee0cb7fc7aa17f64118da47a13340797644dd03b3305d22cfd8bd5528642b1567445d0916131c45453c3eaacd0ff5244c51c284ca16e673038375e7136fae14f475f5eefffd7ffc0fbde9fbbe17f03db7a430bc5692ccde5c88e399a6a151763c9946d0e33257670d4185e5c128bd1a151b38fa51c9bcf1a1866847c18082a493d01f158a0ac1da820680f9ec465289e9290f9ebd2e1ebd2e8b7c37709451601ac6e8c07753a21c793402dc818e5d5979e3181fe10f2a3795063c4f95c487691a15945effb3cff48c4a7cbceb293eed43f4254e1e1dc0179a2ed1313082d03970c2831189338a6c24d0f89956bea687b6c8fae52f7fb5fc4a1fe2f0e631063c7803fa157d4f624627be5c87067fd9079a29a7b25a159e9325795ee62350f8525621ffdbdffeb6f8b22348f9a953943d71b5b4960d6cc8e014456d031d51ece2b14b0e09e44b76345557c3666ac8490f7615f9fa02b6eb0f6b36c3948e453beb491e4a63071b2f04fb870882b85847e26b687f366eb97445956edbe2c7179feee8ade943d98d1194ad69c577e3e0bb58cf7522fc5779c480ada7f0ed1983958392d5c8f4247e57671641c6280a17c350304111d3a20c1127867094c037802900a6159ee608d6cfa8e04da01258696d9f228042e3cc1d15889e9ea9df15352aa6287cfc32a3b69c6930342a1a2ac1f2905b3cb167d5971130a7cf2f6b9a46a3e421a7a44b3ecfca5897691b5d769e3bcfe973b810304071f1b53395760ec20c76ca783472be45977508d3a4bc9e724515ff0d77046d2f3cd1925198ce82ddc2438d3a5bc734eef21b31c9a031219b87eb3c48a737a7f37ba807d3bccc49e524fce33ffe83e5724a855187e92f0fc67ff9cb7f358f1da9a97aea35220d079ff3209886c2687cc90fa5fb281630cd6234b2f01098519e692ab3014a8c06bbadaf0d88fd16ef066f7f5317e4670e99ebea4c9fb3d468c8333f1eb43322335ad1a0bfd08f4bdcd6e918a6d94c63e189103a5cd696ed337cc19a994e82e93523b15185ebba6332b40b8b2e2fa7292be0ba37bf21082ec85307b9c3ba547c9fe94b8b64106684376c9398e8cd72a4c37b5386bea67b151cf4ba225095961e8ac2a7005897f12e103434a8a12c69e46f30409f4d088d86c2a7e7264e01d3efe1487077d4a0999ab23dec13089afa5d53af72f5da7a909663493d52b951212226781ae828ba57c15221cac2e8ab1cca1f1e8c649c57e43100012b9ee9c1305faac933b8f68df3f093cdc16e743689650feb2d37bcbfaa9f63fd8fffe1ff566f9d93f0d8cde60ba31ca3577b8b2ac8260b534cec7ea046468fcda2db7e2e4c6cddd3a8eb759f747ff7dddfb8f17ca667837ecb5815f7ed77ded1c992b73d3a517604de18a073a23361c3c3be107f3662580753a674529847e7e3738ceac0e8b418e1c9e8af00e0991bf6f02a94772ef125a3a3e8f8c3a67ef64843a3e11ee80d6e1a0d23388d9a06c58f02ded0ba95919493334ff5b03ef65a657796d48f7632baa178fbe4c9919e3b7aed9e7519b86b5955799b28fcfa8a9e8dc77d942108c890099ccff5112bf9ecc9a71f697a5aafbe14b277a937089b7beea9f37323d648d628c94caa15b1db285c3532d6473898d309f4f8eecd4020e8de51947d8e174a199e42a742f1eb8a3e8caa2c2a1053c0d735b77febadb7b213a591807381544a0a1c4782572e7445a49176e8690882a8a47d0284d197e76c7cd5186b8d4c7d792cc241d4173d3d826f1e78b2f9a12d6b153ea3dba17470615300928c8d3428af3b318850b6195e5e801feb133f87120aa3013e01d53a9b344d131237788faa2f68aa7a4fe98cb4f8842919d3605e53e18b6dd8c16454fbd9cf7eb6fcd33ffdb3379062fba2d1ec96b6b7ef08ef0dfb0c7dd11f3df8fe44a69a04ca974e849184cacfbad565260264d22879cb8063635d1798ca71048ae7a57bda85c3041a657790e0513ff09f37b5981168dacd6317a67e6c34d199b1ae635d851fa0895fd425aa83c53f5d7794e5068ca0c6e32c29b3a80b175ff0d77ae3674a247e5514faa95cb07f0e6dcb0c339168521ae92476c4ffac66160f350b91c1e60fdb2ab126d964a354eb89eec8d6cdd7d1c89c3e71f10965194225a7521e1ce45b9558bb64fd140233c5d2721ad016987884e240d640ac57684034b2368fc27de7edb797bff9e15fbbb77683920fed181bd815544ed5e884e588d415b1296015a05fe1d7e602af493085e224c8c1b343f7f0dffdee5f6a24ad673de2c1daa5d3d183caa7b3883a9dc1f3322a0276db633615efe1be80e2f4c0326e081267084df3fbddf84418f60dd33407abcf4649783153a0217c5deba93c4fbaa2d1885761f44b282a6c2a3cfec928b8e351e825ed803102f11c8b86846f9986b1dd4ec74243206c1d65edc47494b709d00fdf629b7fc8efd61dc9600a180be0e535a21ada2d358ab687635f8c98ac9fd8ea670a070f463bde3d332f8d54376fdef0ee2af7dbf23d8d93e36ef0c135f047169d365e347f6b5ae5ab3878f806bfb05460c641a7853f587b737747a0a92d332d7874e314b5e9ed5adcdb3e2f198dd7728d221cea917175e3f7d0ce4a1eebb32dcd6a60e8cd97b8489c56c6cdafd85b17f8305b6a45d63559634d77f060826398c61defe7dd25767534c0380f1bc6f706dab28981a3565fb148a67172b29dc39fea76ad14d3001e003f53e3c50064d2dbf9f4b0e8bab7b441ca031f1dd848a14766dd9107d69fe7171f99db6b5dc57a85efb960cac434f3adb7def274d56b06f1a1e252808c243936c66f631dfa199a473216ed79a45546c516b9bcd231cb3e278a89fac4bf440a0f9d4d434e2a13330276087b1ac554ea1b6fbdbdfce8dffd3b3fabe26bb07b14c7ee54d21436158be9345f0540e5768591601a0295fac953358445fe2d5f62271d1b0d0f991d7884c28ba8cf9eade7186900940f53f4f7f8b21ca687251fdeec0e33b3a133e3b40a87963fd51703f1b88293241c18e61d31cacfe68b969196ef2c697dda169ce5cd0cca589d1e0fe4e938783b9a5de2d7b491734d23f0558dde3c7aa153e42135e5859ecdc7f68b3b777ca57f07a7ab701a27399d9fbbfd4bf9b8c844ac29a37f285075e340ebdf63f60808f492ca6e398a043eae95b602c185e95893ad7852948410cd4cc6e3201c0b2d9583ad766b1489216d7920f93ff4c9443128347f17806f5ef28150f146063d22bd6f8efb3cf0034e8f56ac4b6020230f84c3c8c74e17dbcb34aa1bd7f53b5a7a9ec1348646c543611a1feb842e50d626ec62ddd7e8c64154f4f008a2ae26a7c9397570d11b1e8842cbc7aa24f7ef6bad207e4c3113a4ab22e8efc66e7b0218dd88f44d2153d0ac33695afab3f150664ac5b3c0dfe931c6effff07bebce6e2615e8473ffa7b8f608c2496018568998621bc79efee69ab5d23df353504462e2a1d2320a73fd82ddcf5668435b582545c7a7ed685c88e6f3807cb97ebdcf5684443b49eb290f38b8c7cac216958e0d3b1ddd0e8f4fffdd33f7914e2351e660994093ea70c590777fd62064043c0379407eb337810b0c3a399ea141d20fee73926bba33c0ee017625852d011f30641463fec49c09dc8e39e907ab4a612737e91b53f1b67be9b0d97c6953f7dc44abe7ca2c3da877a306fbb8c504257e1e5b795237e7433103fd0c6741125ece4be5b262ed29f9cc5f328d6614c0f78ed250ecb346536c6a2c498ca6549cab45a186010d3343dd35245628ecda29866cd733876cc383b78f88a8e1ef17c0c1239f3fefd877aaef6813f376ee881b57a501a243d312320c1052add1183be716a0a134b1931c0b7de7ef6c6d485699a762cf57c899e183c7cc08840e5f3ee95d64ae935d3702909a1e015cbc5996d17d36a0295eea9461336153ce55125071f3ad6b3740a3ff9e94f7d5e11627a7aa6b41c2246f6c58b782c05e5ca68be9c4ad1482bde4f8403cd5bdf78cb8f2f38a5410363c795a9251b2909f0c908c1ba8ec6eb464667a03f7cce48c85a8db55f074650463eca298d0cd71c7be302dd79ce882dd487ac37b341e6ed6dca00a708812364945f2a5d960aacff98ead2a8d8a964adc8cf26b13ea6a3a143a05ce04d400e65967bfc62ef4b06233a01bfcc21560bcb8533e73c1f4fbd6fb828511def70c44aeb607ecbecd0cb1a97a0115de428f45c50198f3541f870fd92e9a2d90c16f4caf426191534aaa891611830374c3453c8356456498ab42ae8443e9586c2e3dc1b5bccf484140ecf7a986ab0dbc40edfee5e5e65809e2d6b16fa3fff5fffcb8d124e2d8b8a9d9304e924ec70324b3037a624341876d4a8f8bcd345a0203916c4763b8f13faf4015bd67775629dd18cd3ea527904ec70900ceb20003e78a60ac5712a64707099ef566403e01b6fbdb57ce7db7fe1ca030195d223399b307e0ea78aaacac03a97868d0f5eb946e79046e54e4d0d0bdee4f38229df2bcf691536427ef083ef7b3dc76846796038a3c61af20c8b46c463107e50041ff1de145341bec99786c43aa7cb934ace0f40f0aa098d0afbecc7aacc1cefca668bca017ee42bcf75a170910fce194d7b792cc0e38cd77504ed158d563c3467f4e23b4e58a35b77f91f8f5a9678f81d32411475a031a4b3cbccc7cb05f98cfc9e3eae8d0aa22aa0a2b76e40abe1a5715176cd7f5419ebb0a58db23dad55bd95ff402fec82c8ffc077e91b170ef087a74c96e4926dd6b5bbd802232e48e605ae8da3d760872f67189f3ecb14a17b6ec4d939ba87179cc401a128a63fe0702645e3622b9ab9372357f23897a6b78a359ad19b337d21e058a62d2c80596c53706ef0e2d70582284d48a65e04c54d2e9cf4749c558437a3005be504e4d273f24531ec3e3e7aa4675462d6159e9d3ee28ce2fe3a0398997564b70c9eff7caa93dbbcfaf29bdfe8fb15e9283442b37e6086c42fa4f8a1a9e8b18143b879de946750da9b70c5e7951d4e32dcd128422562b466778e87b3eccedd549c864b6f49a36126b0fd1dbdc9cdc825477b5a29bbf0b74702fb9e22d7e6873a367e4a960ae90d27e9820c1a2e1fe269a41ae1d5f839dfc914930605df74b46b45c77febba5912e44bd654941ba3120d89a91f3bc6d7b4be62ed4b1e233b75a9cb1c97a22f9daca20ee4019f031d03b6d32170fa848e8cef18e124cf5ffdd55f2d6fe819648f6ca6a59c261eabbc09380b203e6571c46a5be57456ebc1fdeb3a30ad511f84e6d30a9a440207dc1524952f0d4fd34522274320384ee6cb60be909206c58980fd637e88225f5b1626ae752e48f8343f78200a07b63020c098aff37c8a1d4694a3e745168d8b91209520238dd05d389ca9a3e79b17eee4115a0f7a55e4255443202d1bd19946c68896354fe828242a0595965184ed7176e4f245a3f74c4743c4122969fd89b7535b1ad3d62f348dfd54cfad08d8c8ae1a274898aab27b482f47a7c1c602273c68ec542e64339af25afdcd9b377cecccaff188df6dedfeb961798a97d181911b9eac89eec8a62baac49c25540f64bda4a6d5f5a6021540597454d8cd5410ff6208b6b2c6e58815d376f2088cace844a3a4f3d9df67e19fd1c53305c569c4e0bf709e470cfa4e0ee1f20b31fc842da3624f37c1e1f9273a500e08a6bc88765d1110ef8e30c31b4807fcdb77dff534fbe38f3e591eaa736656f29a46737edd930f5ff494baa0e50202ec884d39945bf31f6568bd22895d41374ef993b7a5d900d956bd3f927f9adf4c87abb1690ecd1f18f1b1269b91a8f0588de1907b4d4623532f04a0cf31364d988299ca47ba15b1781b4b05809ceddb1c2ea52079b7ea48eb112a0467ec1ef1b5d56a6ce141c9e498128b7636217a8d001cfd3a4407a5cab1c0e3d060d053731cc7e712d533bbd7969ddcd992bea8cf75a646f5c70e1d1b2c547064c3d76ea902897db6ce0d87af2960b380a92053476fae88d723c944671ee8d278e924d87040263d302f80b2e0a762d2d373aef39ffff9ff972e37cd1739d974ea062e7b6438959d9196d1ce5335f9111b197558333102715a068ff3c7260f159f1185a9b0b0ac23f6ddd1e849637379d86f9c63d48ea41a3e1d0c6f95d3d1f2109d87da79eea68aad29208f1ab09b236e79d8ae236d1ea9dcaa50d58d5967a3148b368e482702feb62f476126829b5394c1c35efcf8d1071f79b387ce8a0ffa6787b4bf5a0e190a27ea01a00ea9172da7e4d3d10f9dd4d18a9eadfc3d751ebb9a711deef376836842d6b7b08c8a8ebb1ea2bb3aa1ce1c8dac058fca5a0251d60255193d1717315facc3c81047c0ac2437db3270861a24809d5a8545e5a5400efd3d1b1486463355401c8753d504ac05d322d64e8c7e37b76eba01324573434466d9634d66a164d1852a037e8c922cf41975b0057c8f2c7222bdfc9e7439d0c684262f9eee711c8946492502cfa16c4bc206f9d40a0d84293095f9c9137d1fa1fc85efb087d30d1cd162a4bcaef389ecd23133f8fef7bfbfbcfdf63734e561b19fe74e7ca7871f04ebfb46a8d4dec491306ca531c52fd2fb585fe8233ff1c098af02672794d7766eded0035f75526fbff3cef2ddef7ed78d848d033f8b5385619afab1bedeceb307351c7664d9a9e5db8269e8e88c631881ae6aaaf4f5af7d43bee7d75618a5b4ae7afd356fefb3817156a31c6737d19d697c87e89aeecaecd43ba5acf038f1dcc177c5961bbbfe59b870069e68c161ed86ee8cf8747e70b07f35c25dd763048e9b7527e1cc13f520e8a68a2e558ec8756016808f83e8fb961ad919f9e08ca6becfeede52e7d587acc12a3a62156d1bcd258c94396d7c8010fe9bc369e052404ea462b0a660986634a3d0e55dd3c529151db7e2a9742b001e159635168dec8c46471ea812e0e7cd0f35061a31533911a621a8c7071f5a7aec8418eb82428d889bcc0716209593a922bd211b1b340abc43e5c836b29e9749dea1a746d528d5c0ee6b5a82bc9e4ab9508a672921b14c9d725ef1554d972e68514f65a071727fa45ef0bffc97ff47df13f289f8e9ed654d11bff6f537f5ed55ef882ecf7da8f4b892118e11676beb4337a8aebaeee8e4ff735a53bd7849affe6bbac9a60c53befffa5fff5fffca0ad3e167f225be6523e71d35b49eea6e6b4715fff15017417e8dc3ee39d6e607eff0e93b0ee5a3acb333c2bfa1f5d47ffacfff59d66df96be018c578258732c0abdea955047740eb027039505fa05aebc6a8cc2e0e2e9da7a842d73ff3501a7cbb19fefaa333a623649df8e1873aeea4d11a2e3c5bbdade92e9d21a761e8943aa4f2470efc57199137743241eb6305a3bb64f2e53a34b4471fbdaf1fa4a0de51dac1812c32cc203a3b3fbc5afe18c982d6c8ada8180ad37f4c17f4fc854278aa699dbf235f8ed56acdcac3304aaf0a6cf2546aaa9c54680a9d0a41c523e04cd642ac63387e43a19a9b989fd728063ebd26953eb2a4e770deea4c78911f71d8009abe0d583d3c85c1a98e178f9902062fa390a63beab95817f2b85e75de5328366698c6d2c8d08f29e30888745a27d895cfd714c08b8266278f06c393227e6ce2673ffb850fb5b2e867f38143ae1c6be20405a3aa7a1833e33511b6b819bdfba4040630cd7bfbedb796b7de62dbfeaad740c8e28de89ffffc171e9d281b1f7f53b9786dab1d49e8d4aad430f2e88486ef29a0fc80f28c2af884d10cbfa29b1bbcf01919783114ff753da0a27a34b5df712199c91743fb68d349ff9bb23b7fbbac28f245ff52149338d08202524a15a302ceb6daf674cff39cf39c9fcfbd7f6d9f73baafda628bd84e3880400156c920a808084551c3fd7ebeb172ef5d257aefcdaafdaeb57265464646464446460e4b6ce293be69e73119661a61da286f02632704b947577105ee6f94215c4c4e5b644ec8aa90623d9c3c75b28a121e0d5b1b815dbfc2f63c3ce1612b604bbef278d3b75370f697856e51ee37de968f6e646ef29ab4953069f6f82ebc9988fee193a69bcaedc7647b0406cc241eac65ae574f23a611ae092332b978de2c8958c428e8fc59cf07941a0222745e420a4374694cb4537710275ee33115eb61cc18e128036a7818dfe86dd686c26e1f997ad4f66d5dfc49ba433a572894172cc1c6d83c5356735c8a46d72be77505c3bc0d21b66e511cdcdfaedb3c1b48535f2b302656130dccfeadd4cdaa985bd3181c1a257660a8abb9a2b50adf172831b5faacc9774ae5e69b033b71aa8081ba113226aca98b96965e48fd4f9dbae7e8f39fff5cc771ed4d9241cf645c74f6ecd9e24d09a9b77a58d06bfe8c80716cf8e9e12c02e638697d421738d856224e398d1f726ef78471138ac684027a8c68a23258d3221acaa08a88ed5d639a69e2dd2669f3358b07ed33cab0b4cf9f8e69124f98281b968d71a3f9d52f7de98b1dc7daca636b0ee16befbd4c5640421be05b9802129630b8ef30628bf7bc25deeb8766090c530c514cd7862e17de7a63ea30e000693670b5b55f23aa2f53ff3efe15177e0bdeca862e0098922616b8a8998cd5dc8027f4efdcb6422d38f1ab7245627bc624bc7ae64b64d1885cc29637d1ac7a94ae76cf5609b460a66056e3322e6799a68a32efc767ab8cc46e6198177c662e7b1e7c7362d7665c8310c62b4c1163a16319df2c7cc7299045b4496f3217b312844178c0c3a14c9547c2734de6e02401435af860022b346cd0c4e89752683d8fc1458fa37719f773a63732a6c3384c4293ee48eba7f78537b868872911e6031118ebf9b4030f1c65427b3b70c8c25c0e1d8e164e1d5300c663a633169dc033116eac6a0a012efb774a183396708a376ea6388c5d5b5fc286068987a7e092c72be0f4c5557f9a46fa6454ffc24fbd6cdc44fb39ccd51ad4df677ef0b52a48caee739ffb5ccced4fd5ba185c072fc30dcfc5452b6cf787c50e9ed33687f1733f7598fca945405c8ad2ba367c7a7d8e5238ffbb38c6526f419a150607915b8c6bcadec8b1efc95606d7c53473bfbd4926ae633d90462664b4b1c676a0665eb7e0a9c43c17d256304486f813213d6f6157e4c7d4aa232544b2a2a15ec0ac3be4913b76cd7c45924d6eb06d227b35ceaa55610398d0cae75669bbcaab7d0404033a7b1163d7348ac92a9dfa308d08bd3cd245268b937d4f75ed47a376a538ea045cebd2fbad82caf68f164b98fa629c6cdd8900502684d95a4b34630918eff919a3e981403547f5c10fc1253babf3c1704e18b00827e5623da6f7049e86009b0280bfe3c6a7ed2fb7177bfc078f1ffdec173f4b1904282678ca1e8711a534bdfbd068364212eabb4e9c28f3a281809e96b459f5d0c5d31144a6a576f265d04ed607bfa1c2b4014accff895d0c599a6cede41ead0aff62f04a6fc542315430d9fe9bf4ccd644f653b6711871f2981fbcfb5426f71ffc74153e7a99b42eaca978716ee5c09fe2274e5db63bf19347c4c442abe969ddc41df21157fe4df1001b975d8a221bb80bda005d75dc9e72d9843d8077f3640bd17de1937cb9f37115130113301dad5fd4f07a88b8d2062959607b18d6f3e07ef8a6e398994fb929036f1a3e5a329ac2aa0fc43e9ff31ecdcd2da2d0ca16c596bf52eeeaf209caaa72f14f2918de7defd2385af442ae7a326e6fb8d38ad211b2717ee47c87944753b31b301a812468f03a1e81176a21ba51a846f13f654d55f31084a72124dae6f9d20beb75fef4d6b882c166a6e93d30bf5e0e1cc24699381b83eb9ef70f23516acc68c262f1ac3a633c66212550a10b02d3d8f16abef36e3f92c131b296faa8a73644b7524c7a080659c722e8e9089271d9444719c44964f5ca934f3e79f4fcf3cf770298596937fa3ffef33fd55ced17674a0b24999b5e738b2e94f3d06426cc295475b6eb599dccf739cfdf84fbabafda0d9d6d46198a54d037185586a18dfabe9cd3b43e79223bc0a35884906dccd75c4738d40b1e724d58f1c3135b642ec35b731d4887b9129f7fc7622a5e9ff9b87ec32c4206686ad5b25a4a9fa7ecc2dfb5c3c47559d5bec87527eb410004b10899ee3326114dccf9a147d0bb2976aa75983719f3b8f161dfefa0069e6545f60661764e80a46e78f7dcf9084316fca6c13f90de4e5076d73cc6a1c009a19102a2445a25caef1e41990e133f84e61058733ce6737685019e60bd1fc6366fc51941e0197e1584985e18a39eba9da60bdc483b37bb864087f5cd80e331f98cade0836e84853231a6c4507d11f8ccd0d9d66f8ecaaaf9a9a7d519bc927a3e0a8750e8c98d29ad72a0e0089bb1ac35975cf84ec2121613b5eeda2cffa20327bef40ab67959dc0ed2ab1f81674aea155750af97c2d48f6735cb4b313fcbeca9f31fafb7eac20a9477d21e719680b565822f45b8141565a99765a110121b37e1de3333b372c3d6a259749e3c104f5ee6a385ce680c5f8ac22bcbcbcc8b69234bbf08a22106fcdd6ba3a524a6a283d5a24b40ec02fe398c2fdfa494c5c92d30a92f87bfbb5b3ac389775f4ffb45e14a3b905d27c70ef0ba8177fe5de15d3c2c50ba36c6402a20c43326abd63d72445cbaf934f8e57c89628730c0101001465169697dde3545a2088e06350736639249626de49fd27bd074b744a8162cc2c5a4337027640b5640ed82b28d158ea5e18d6f78d7ac5cb8ed368b513fdef10b0785255dad5f72caa35e1d97253d41a868063041676ef14c9a709e90ba250fe6a1d569636306cc4ef3df7bdfbd47f767bda2e563c527e3990fa70736a654978ea79c601506e5885907e8b4370ac5d493096845bd01bdf92147cf7188bc9eb9bbc77ff08330b853929de59f3322834e7ba98de683e39ee670f06b53e4a5965934733f38e5901e66a53162ca034fc0b068ce5bca4c9476d58150ce0a1ae3d5715e60f4cb31ad31be33fb396f5ec9dc20017d2de342b4b25e12ee977a049e52465951e075a814757f4621103096049c5920a61b5e7bcdf86826a83bde0c6cb83ef2c8235dce557e6a33ad9a2a67ea3a77f3575d4a9b83772c15b990ac77a99c03766e081f9dfbcd99e2122278b90b8b2e0b1600044cfc1542b6cb919b16de6448b0cfc0ddccc328969b9d0601b08522ca96379784113169a7913d6ba4794b683520e6211076d5229c8f9933d3349231444f8d0aace5a0e0fcd00378079486e17eff40bc744c2dcc6c6577852a0245788c5db8c599bbf55cc1ae541c642c9c25ec9da35af826cd720a703a94f9da28d6ed5da8fbfc7b8f7defe8d9a79fade70b83d5fc0d9cbb4fde1d6199359255263175f564d3a834619c1f11108cfd46be30f3d69f6ea989a4775b2b30befad5af76113006b71f0e5d9ef8e9cfdacb1b9fe9458fe7376da53ee8da3f6eda264d937a1bffc28d47574fce2c3bf3eb33a95f164c87b9092b989658dd75d75ded9dc0ba2ef4e2d1b379f4cc99a48f296d88a0ad98d126ec3ff4c10ff7594ff546056014ce6fb29e93f2e1151cdc369a87e4f5566707381e18fe304e0cff444059112b503ca67b3e74e338c9ec10d7b6a74f9f3efab77ffbbf6b66ea4de17ac71d77a6cde750a5e3d746c1a185e6ddc0a113e69b161f5c7642b115a8e879efc6dde439166beb7aca277ce4f3ceab424ddf247b619db881a2ec0ad99505a9781224a5ea6ff815859a473be74734721a1dd111a58ee35ce56da32b690b6ea748ef440e42d259be73f3cd364ec6048cd03231682d7bbf0cf235fe98a33341eae0945bd32b313bac94b0994f4fe5645aee6a4cc499c2b4b44a8099a52161d3f1639109f9174e1b2d1c2d66d5b9033897392a0d5ceaa088d05300e1ecd601ee180d13319bf484cc421a5c6fc0ed6ffc3861beb8e96b9a4c418c5c633475a52c7ef18b5f76fca4a7a0ed31bb0383fef11fffe1e8c4271d473d6bf22cc9523fcb9aecfa86836035bd3624cc33fe51d7d02bcf7af0fb1f78e0e8ee7c6082ab9fa0518c4e1bb612e5cdd0bc4c9dbae95dba8e31f5620236a40cfbba46f9e8ad2ed624a3b898d82fe6bc0ec2e7401c38d9dfa6a7610108685fbc36654068e0ed4729a9025cfdb8eba565f23105f5e6cce65b0956dad7589449af4d9e78e2898ce1ced6d4d48359aa472973de505e63da1785963577f35759ef1756fc7c1526699a2c08c29d191a21bb364afcbd2817028cfa83bf9b3d44710df2e7a142b61a4bd9bb4cde3767f5415f785e441b22f98672e6ca8ab47792ad12e679fa2e49e42ed6dbdf49cbd1402068abcb6f4d6a95a5fdde78c3c6c99c8e950a42056cbd1e17eea953a7620ae6bcc234841e88e3423abd1e65d0a17de018f8d784e9c07094002d0a4b355bef357019232812eab57ef2724c1abd1833c7140053197e7a4fe55a6d7ec38de6a83053ce4189d26146713fd3fecbfc910fa31210828508cc205bfa7ff8c71f1647e5f334c25f3e6b13f534eaa40e890af3e96d3fdc670dd9ba86c1d051a09404b42264cafccc673e7374fffdf755796072eef15b43476361f5521f3f3d82f18ef11365304d19218bc9ca93f8e978f54cba9b9fd263d8dce9fcccc7bef7bd0a1833d84f2ba3a11e92d004f45686de6aeed14bf00c579646cde2bb3ed11387294cb462997847f1b1424c23516e567758c676eeed7cf236de6d43046d669ca71e1d57aa400bcc25ffca9b9e13dcabf32ee4793877c5cdf33604b7ce6c4e178e2bdf37dc7a586ff24f9e40b90ad67af63ede45054a33485c5170c9b5104aea24c4a09c086cde7eeda5deaf6d2d2040098591fb85ee2aa32f9b60eeac15a385b8b7798a2c8f9109f39927d323b862e61228d92c447d284c23b411d9f1296b119056b664c840bc75226e840e3eea19663596e2b5eb5ccc669ed937e543e667cf9847627e4c5a70de49431a9cf3d8dd94f3f80435a51c300206a73d95e74a38f446172fe5409b94adfe98bba671bebb659586b470bd94759bf280a70e4ce23252944bcdc6300c27ca8439d6fb1361c4db33df85f1eeb8e3e311804fd41c7cf699d3473ffce18f536e7afff4ace8cf41609e8c02803525a00d091f85e5acc7b65770391787934d9d4c58f365c1bac5322f3ffd994f1f9dcc3a4bc2b38ed9e6ac60d6c35b5d944111b42d9a93d08f79e951fcfa85ba93026dc24b3ee8f18ffff48ffda4939e1bfe33373738484f80f5dad89f09cb7a71fcc1c0baa665a119c5615180366f2f1de216a7e0989b96db3a0f061b1e838f2485979ba6ccb3b45d209ddef5c628973f3dff6ca634324cda6049df7c037a2c9eadaef26edec511b055e622d2212244068cce9545308c6f30a91dc088cd74a39df679609b1cb9c06534c92a2157c8e73d41b1f5e34399fb21b8343962321df5209d1f51c9aa14cc1a21dfb4f65021702238135250feeba7aa41734564da9d67ae671486f92daa35587640a65ec5094f9885a9d7b14299651a577e9ad3940273aa66d346407bc4ac14b939b8bf9e2f6fae006ff33b9c200450dd11417a7bd7ea9adf1c3718017d4a0df7f9273dad0c37789933132423d80f3ffc48058bb6860fafa98501cafb5936b6befd769c51fea5173765c19ce5a4a21cb5adde8839faf18fdd7ef4d4b1a79b4e6be9cd8db128944b9fa4009974a17d68ae5c9e60f88913280e5b5cc07a353dc8e29b75a52ca5bf318a49ef6b425edd9995f3bde9e10dbdb47f1c5ac6773ab9b13e4295cc95a60976b0bb4a2702a8de7ad4a79f79a6f56e9949b8685fd3337c2a7ee81b847737457ff767f1ecc21ba59b34f0d05c6db11f57fe0d190f76b7b47160db6d701b7a4f7a80b7576e97e363819a0c0a53f02ab448246e103657166ddb0aac951f59701b4c1063c2e487de82d1129a6087590427da2f0d6ffe4b0f457bbd6b509924cc504c6d699379a2d1c4035d031497242cfc30816bf1d453457b122cfba66835939b2f6610cefbc73cc3b83c7bd254d305acfc1a90701626260d93202e067dd34ee9085acfd048fd55a5bd70342a4fe51fb26ade7812f2948f71099317732e8aeb01ea8449cf30ded1a98fbfe09532f9e38ab9ea88c87eb2db3f7e477b100da927b1678bb90657422a039ceb1d0d2d99aa83fb98dd7f8cb0eacdf448e8eacc8a71460c1d99f280a09f718d7119daa89f36849c9a05a55cc7e40be553e6b1b45d162b67acf2f4e5086bdead7662e232ff4e9d3cd9435d79748d9f29bb1ffde847dd71ad97833707966fa7b126c6e9620c1a8cf48a5466d0839b76673e5386c6e4140b3c26c070be5863899885128e1ddcbdde526dc0d653afd3de5744e521705b6e2eab880c69aef3418a2882f7e215460da5ce9f29aa3c282e61dd975b9a3029dfaf3009a711c01aa6c68cb422b02aaa279b92401a68b929bca221aa385f8575b08f32acfd4fd3196caff92f04b5fd62c642391a3cda375e6f400b778d75682ce336ab056831e3219af2e57c0cf0c5b88d392608abd5da7a49f5d1304c48bd68baf222e7e21da153f68468f36c385ce31de5280fa3a3151ae84d3838c8d7083fa1cc06d1e0de6f8045d39759f29e1964ad2185f2c7d7e7a05125c9bb680fbb20529adb98a82ec3f0d91b86cb43df317da5e3f418e70166e414f059a2577e3bdb8102a6b880e163851fb8f9d5f68eaf6463e9995f9fade2199c37a51ae8a613385fcc0fce3ac68d91e059224d9be5b18112e1f163e6d6d39c58e908a8a317fee9fff8e7d6b90a214ae19dec17b45b9a62ad621aaeeafc1c210343b9e86c61b6674a8b62c40b70e3f0783113d77a7a4316a1744bb984181c96877165b0e9fba609413c69ff7dad9217a1c4ab5f42793e57b64c63fc49927e90224ad582e173bf7d29fc64fa2a30573ee973dffc0730e3f8c0b40a990272b32bd47ddfcf4d5300826033b09d951f0851cfdd018c55f020b9e04f6520dc90972a4c80b8e569facb9763e347b3d2c4e660b885119c60ab2937f67bc65421a6f92b6388d7b2a68c6dce2ce2a1239c3c717a402510a62554537062717ee0112ae666f1dfea66acc37ce50da48dad0dbc3333fec6428e225884b564aa265b16322f26434bce947e0925f8497b5d26557353a1e44a77604c4de3a4f31ed9d001ad90461439779c9d71a9ba62bc922d2ffd2354a944e1a0f5b5397f5bafc274eb969df444e0a9df99ec24fee3fffc9f55468e379875a11ca533ef5978497c3934e99aca3028a636dedc0d0152af55ef801dbc5382b663325292a60556a018580078053db58579315e580e2b9f55b200014c66a79e47fbf152ea75adcab16671e2a66df5b0f02248f2e92dd7d0c13385c1b4b727cfe4bc769bce60b05a3ca9be2bc8b7c2badfa5f3aa8de226b9727f0d9331f3add73e9b5ded7f1a577e9379b701dae71f9a1d57e07ab90a5bd755a8c62c5eaef9e945a682bec29915f921a60a12bcc1796016fdfc5168ef95a4b05dc53c8c27ea43b1b169b7d7d250cac5f4c6631a82e0804fbb3af7ef95f452560c68008374ab1410beaeecad3e70b40953d9c6592a5081daca2608f0c0da7ad01b33ae21540e7a99b996aca80ff37025f3a2d1cc845518ba0c73607e83708cb47a3aef7db75a43eb5dcfbf7bbc6618f7f2e9d3cf45cb8ea901a7d5200beec046a6304c18af9eca281a4c8c61e599b4b90b4dd49992b369d15a3f0ae6dad439fd5bd3f9b37a812a933c13f09a755b928583f7764c8381e60eea31b532ef47436f0db8a1619ac0369c116e42d63252fe7bf9d4916f757366114230ca6bf104727219cb9e39336378752694d62b7eeb9bdf6c5beb7ddf4acfe5db04944ddb2a65e9c5b583aba1467bf5d4a96c95127ce34d2ff6bbd75ecdb4c5dde1c9f466812f2827d912f6b4e9d3446eef373ca5c9ffc933e9db364cc628b2baf2dfcee65cd09abf8cdee755dea2eb584bf36af777012e6116827dbb894a80ceca0f3300717ef4609d0cf2b706916a52e666d547c4e01d8d00fb2d516e8dc53a9e08b30e15f232e5129ce79f7f2e638c59d06b2586710a172da12334ed01028f368493b02ad722304ecc2c717e14c1cccdcddc0f0f951dbf16bad689d11e95776b1b9f85b18b2e3af4ff5e3054adcbb1e298b0fccb78c6a4fa4d5922f5e1aca2274cfff99f599d11f3c5d2248cab775ea7620d71f6f0e0bb6854ad9cfa77de298a86f0ebcd696a2e763da5f93b4ae7a59c0d68ccd98f30641de38c61e3440aced00694d221a4a503592de572c97b6509ea0cff9613c74c9d1ea97c99ab29e64f4116d1e4174d06890000400049444154cb7b4bd1f4f868dbb2a3d49443b88d878db36eca7ce8a5f48670b839cad4e65667b6cc317d5180e9e938a4eceeae7778c3493123547aee9870f911640fa31406a77a2207b12ebde288a1e0ac98d14e0df20e41e63138c273c5b917f0d4619cd85d36ca360ae2ba8fde76f4ee1fb2c4ca50493e65a77e122e3860c9375ce9e920ac4403780a6ed11b521acc9c05a2824f83fbf10eed9051ea86f4609002e0b0e292af9549b4f2d8cf5cf51adae05c250d702d06e55ac798abd1a5d75851c80d83e11006c32c02691c381a372c9735d734f7aeedf4263a99584e9562eeb5a1da24d39382339a7970f46a1a405d525efef19611d0871e7ea86618f87a42cbb98c7d9cd7613cc4755ebc8313bc90017ce69878f7150af8c754568f887abdac18effa9ce6eb1838f340b6dbff3673357f88c2b1428479b5ea0cdef4787be1d15ef04ea22158ed9ea90bd3127dea6dfcb863c173406a049a60e8bd4b83e219081b9ec51da49c59e29fdedcbc166123f8de6b6b02c432b1de108dc58161f50ada2b774d971c5a19605298d2fa411dee68f25e7aee199a8479335ed6bedeab9ada49caa1c2f1c4d476a666e9218197603665ee13441f8645c75ddce17bf9b51f577ec6a1ef9c79bec31769bd92d40fcaab08f7ef2b644992c2472257a1931182d365fbb287f31d30a681bec5b18850330400a115532262297730dee1b0dd20a489e4e579a3a58b70f22fa1611acc51cf2a314cb918a03d5a608967fa31ddc06293dfeefbc4617ae7bd8b33012b8dc601b38d08558c1d6dbaab2fc51978cb1ce9fb9898505ee3b9dc168e2fb860528da967b3d2446dad4c306663ae5e73ddbe27916f95b3eae0aabce965e7f42a3d2c8d4f81fdef7ffbd73a73f462c69bc64ee839ca667addc222a4e89e00deaa9fe78975979087635194565238d0e781071f084366c955a6192c9b335e0b84ad0953ebc06c7eb0ab18bc1e7e80b3dddceafad69bd9719d78383856c2d8ea70ea43d1d76597b13199f6f87dc65d90299edab0146e546130eb032e29268db635ae45efdb6fbfa3c2fb4acc64738fe61b25667a5ade652ae2e29d99c34d1eb8830d4e174668c83f0bada1aab6ccf5bab1514cf080de35a1d1f5b7d9c8996f98e56889ddc958c9d832e4df32cbfb17856c35cebaee1a0e9894a671312ad3825bd5e0bcdada1985ba4d21e95a98c7decc7361e5596504afccbbb55709018db39696ac16ca7b79d64f7ada59f93c7f266bbbbe2ebd93f58a8e09b30487d7cf9ac6f6521d876c5d5ff20beda57a87f81bae9e5300e15e67edf36e71dfeb49686dde310ca5e9d591d97363b4f261a06d3111334aaf4fbb0a0bf7454fcac53c97896a9ec113274e74317015421a930279f2c9a7ca30a61f083d278e73edb543db2768c01773b92ed3505a6195252d927b46095734b44ccdb7d330ef6038ca2d505bc5d226790fe1edbdbab3c89959eb94ac45bb001f2744c664afc7dc35ed30c72c84e9a2a0d77ac8b3d64f36df28b954b834a5986abe863ecc4b4bac289c599ff8f13a65584e94ce4f7efce32e40ae900501b899efb36cecdd7befad73463ba97b5bb9fc39b451c1d230ef0424f393187dfcf3ec7ed1cf37cc8ea7873c1e3f820376427409e6bdeb402a10f77f51c89a6efba38015140a553d5617dca637e84468048de76f2d1e2e6249296b91de8a2eac89582093285a3542c3646442b48ec938c54e8912d1aed6d1196873575bb6c43c73ee2066be254c6d2e88b6ab4916488b50aeab5744acc39f72f454bb92726f8bc90b31d19e3d7dba269af114f7f6830f3e909509ff145c9d7d514c4b11c8b6acd67794d0cd3767b1b209d8f49c04d678c264beb1abd516d613f6f0cf30797b02a66bf067a615bf5008e3a82ff3f3f8f1df0e15536eb1a508f2db959b02ec59935fefa7b7832158d234b82692d98e1e1c0b2c873a8df28a59de7ac9988473daf2a4a548cdaff1ea724c98507eedd57140fd3ecbb19887e8b082323b399cde8c3b9e407bdbb60e6d66b57ff82730919275643c6bb994f624fca60798df7a2e4ad83408611d6756f6ea4508592a04cebebacef3a4100b0f8c07f5a2f2ed837642933d9edec175da7352a22f9ca42d7d9b287f44a6fce36993eb23f8e7ce66ec9b3612b40ab20dac46357f85ecea02bc5e71ae13a661dbf829681c0ddbb82cdb5e7ab04e842c399bfc706e6d22f237152bf2c6045bba416d084f80e6c0538dcddd1b410ec374cc1053e6f610d301a0263679db30b19e401a6979bac0f71f03b586ad7631281135b05e86b673141d1351cf80015c67ac95464aaff5e4534f1d3df6d8f7cbb0b4b872baf223efc018f7f6c0f6b70d0f87e0be045a2fca9970ee282bd1b7a456b69f8c80fddd37be51065136bc862aa941680eaf157ae270cc136732c2bb09830f2b42fd3111a1a5e56ffbd8ad9d57fad9133f8f0975a6b0569b81b718491929a54ea42bd72bc22221c812bc73e738586ce599a3e79c943cf350b3599407142de0df316df0caff968b9684f2f77110581860c5cbf0d3ac5524409410c17140ce898c03d5c17899d2e185648a52e69413dc8bb7f68b3280a35e1b7dac61f501929e6f1902719a71f9133279e0971bff8b5fabe82169bd5f74117f18e6ddca93f4cd92ba86571d7c1ac0cd3f40a70527ff94e57eb733fa10b0fb55e8aad808fe3031502a47a318f8139ed59b4d25201e2641ed72c4dcae7745748b579640487c5e96b964a06c6c634cc5443010afa68f5985d1872935668d9e809b0acdbcd846b094ddf1618842db3b93c2a4b9e54add3c9806e081b239d4d1670f3cf8601b75a185799984cac36c4c17389a46e09ae636bfe14613c4b04f7d2358ef651b3da6aaf64e1abb7d7ff5ab5fc594b604671a93f2b0d0d8b80abceb8fcf968c2594d3a8c350684f7130ad7c46e883d1f2d6d0e9d5687667cbcf87016fada0f1a4314dbb223e67e613327095532c17bd3444fea70fac40eaa579025905abdde5636e7dffb1c78e9ef8d9133157e763f005b4fda154cabcd98331d769ed31c35340f067cebd16e6b70a85308853366be3de7bef495dfe47ef294d4a96f9cf62b962bc1c7c177da61e534e9d3cd9406cd9d95d99c37c31eb39293567b57c24633edb8d2868a174cd75d5af91dbb377c2ba4ae3d7672cdcb009cdf67c2cb4be21cbd2b8f22fc6db5d0881a37abbbcc907467bb2ab0bdea01e5c9a539689cb23cdce03d4ae3b8c80e9fc981b7a3964907af7b71549fc605338036dba58111f8e8dfbf9cf7f3e1f687820664f5652d064196ff52cfa30175d0f69023cd719e71458fea8a012eba94a4f70eebdac028926ebf2a4cc591964bffa6a26acb3ecc62436c6a245bff4a52f1f9dc8716ebc63851038848c070c431b9369e4f3f999fbc2944e18eeee67550a4ee72248cf677bfe2f7ef18b9a51c621160afb7846f12a83077ad27214d1b2af6729d6adb77eac42a00d686b013c13b7e793f7dd3420e78195f05fffbbe9f96cfdc7b0f0a58ccae0c99f6ca5aff57f7a36c205ef46862e8b3e9e95874ec6717051af4b974eec041228ede92cfeb367ce365e7b2b4b0f1a70109d3606788017f7d5a2ae141ca5e4e42c6b0de104b6b93a5fd4f9487e848a15012425346d3bed3c05e5c5164aa7a495ce226f9ed5b7de7ab3dee3af7dfdabdd6bc621c2b132f4a1080ff00c1cf0377417d82d6ef84ba43482746e6137bc3c91d744f9fa1ae70d71a89d8fe91c8474dcf5e2ab083ca79c58491efe5258899a64ca94bff67c58a204af99158259f9cd01d21eac7824e5aacc86f02a6710f00428d407b85ea36bf2c290b4946a556092bf9bf994aec2b9ec7ab10daf29621e78b5ecc47d26e6553fa31a17b81ecb1e3513dc4cae324b1adca4baf117b30243aec6c60c56667006d0cc8e131fcfd64dedd5f45c838c7a641a23f357bf79f1ecd18f7efca308c59bc3f841565da72e535bde32aba32cdcb5621c2e68b8d614621cb81264630a13ef04c0b4c397bffce5a34fc6cc647e0edcc00c1aa3e5f54ba8c9c3bafff4912f8e8af55b8c83414bc370853c98d44e6b02871e2c02385bb8cd9260e629af02b2d51b8c66a6504bfced39f07641bbe5a73e94db7df7ddd7ba8ee0c36994b5f6afd9de98a9c5e09bbf93ac6ffc31d6b4bcca98d03446a732422b0afaeb5ffb5a77451bf3c2150c65b55df304325c87fffa2466f7bcf8bd91bb3fda6ff7909be4cfdf4b1403c7daed77c6957f26e34a13d31b2f07b4325638707cacc82b0b5f09177003e67159ce1804835c9be54517622ad1da1a28b2d2300521f47ade43eb5de21178874f505879f613b6ccde11b81227193b9eb2bc2a422d0e5395a8aa109806e8cf3df7dcd1771f7db4939bcd178672d5cb12664c042ed43099dee282150e8125b21eb0d8f9274f9eac93e3a3d9a2624b096dcced6d1cb4eaa52e607ef0e6d92cca913027781907a441921013953193d66e6638be1e67c1ab611482674780f92fce047383d6e5a99f86d7835cc8f8d1722827010b9e539d5da8d2d910d226988e4b9e1b7b05b0fc84454bf71c3a1d97a55730870569c27a73bca67a4d6633674743b37b3fa6bafa6943419e868332c431a1adce51e7b56d67399ae0a11e698ee6dfc0eff01c80292330098b0fd1fffbb7be7df4cb5ffeb21646346015e3a953a72a80044cbbced061f2c95bcc5ad6289be189c15b19eb79d1675dc5af2075e99cab2a5e13ba7ce4a18773b46016383ffdd4d1b9b4e1a50c05f650a7fcb87bd410187ff6af8bd856c02a704bd82c2843eb4f8f30e7005a5e451b1681c21c011b44c11ef8de2f584de6695526efbcae60a17c0281736ab1b11a8160fe751c110d69c9cebdf7de9b314ad6d9f91738ecf27a1963f275301e3c2b5470deca191c023bcc6f42d4c4256f5c8f0c0852f29963fb877ff887e0e37cc3b8e9d3ab753a2078a149e93255eae0dcb8c258c0042c73cf207c7af6e0948953bd22f3d3aa75fbc0d4f13ffee3d1ee8ac684be06ba9bb4ce3be623e501673dad49698c7afcf8ac7d4c92d27a590f53274a6b56c7df6aefda0b2f144f30bcdfd51f61534f34536fcbd74c53d8552e8d1f939907f7b65b6feb98cac734082f67c5f1d0472f629549e79dc03b08c525f0d1ce989690ead1ac5b546f615d8796636e15a9bc1b7c87b87bfe4bdd0298d50096786667566a759cfdea6baf6627f9dd9deb031f0c650cfcc01a0278d1386956d8973131f25e1d7638c9efb51ef3c45df16ae7dc9998a8affff427476f9efdf5d18578365b66eaee3faf45526f080cd5fb7c58c80eb8740916f0caa53169599a9346391f4db85f91df144983cc1a3ecfcd8e395b4f759dfb40937a88211947ca4cb8d6d1106dfc4a4ca7975e7ca90d6b0e443cdcefbfff81f62c1fc9225d837eb0396330b31f3308dc96bf95978c53160879772ee625f7af321b8adfac643079adf7f36f116e7a248926b97e564fa63ce78bb4e7494f7b43265db9df4d2adf91dfade901997db7fc4d3e031506561ffba14c1380094742b5d6167a1e1a6d5b564207f5c6fccc4bf8328d293bbdcd2c969d3c9c203d5035d307e7b319d3ee61411d044fd32699348f629cb1e6efea48c198e3c8b2ede89aae01fc54ccd43a59520782c2e3c829f2bb8c6d09f9e0ba683405284bef63186172d8b2a92e0f6392a64c738794b23aabd38ce5177683dfaeed120d2f736668793a8c6db5cbd48a12cac737b22d8812ea62ea940d36ab070ceda31cb56f1e84bd2a1c96a53e9e57dc7a3ecc723974bf782c70a3346fcc14c2c7320ebef999a78ffe1867d79fd2e35e082e61304236443fcc7c78bf0a11b70a829f7b95d6b0dd869202b94dadfc0073a5cddd8e105396cc5ba9a9c4d475044f1998c7eaeb67c27ccfc79140b8ec1d32969a31df9049d9566ebcf527878f66a57e7abbb067cbe69932b96b02d318a313c1c996e25af6dc286d88c80ddde3bbd35b587ea5b14be0269f41b63cfef9af6ebcab7017a737c4f0e2cdd97cf6b38fa4678d7919afa5c971eee5aea488425abd3ff8cc34837f6e6ace900a73600a4cc8f465bdb7b7ccfeb757f231ba679f7da60c632b0fb312a39dbaf79e2387eedc9a1e470062962e7db4a62e8feaeaede02810ea751aaeba3045ed6478f196dfb4d7ec7830a6abf1cf5d397ae02b7ffb95ce4da23be56a02fdf6f4c67a6793f41181c2552f82854eea207094587ffa62d658fa18879e88e79269fc56cea13c79f2e4d117bff8850a6f331cfc597c34b57220ee4d1d279aacefbc5cab83f7cef7a83963718e2a7461eda813e5fbd0430f754e12fe6db883320e6ff7e54deca2d7a11c1ca6c7df26aeb8f48f9dbce7e8c3b17e6ebafb547ab51f1fbd11813b1fcb6b1b93edb82fc9dd4f3804bc0a9fabf734269366e6ca206f4cb60ee36c5be68f9e22ffdbf01bd8d6b1714db4628761090b66fac10f1ecf8af5e7b7725ca2e55346cd3f70f3c328cc867af1cec7d44bcf519cd3e0e6d034845e96465b66b1f70b21ed83f9b8d3791a9964349f2d2c0d8b144958e1085c51355fd3a8162a338378e7782ef506cafd2fffe59fbb7a03a32f73153c650f7323c8f43e7a379e4393b5e2fa6be111747952478122206067cf9e2d43316f99968417e3c2fda31fdd945bd2738e70fdeb5d8def84c5305cefd766658ebc940a93d047f428b8fff5bffe674d4cab6e9891eaf3d1bfc9e2df2808bd410528ce7fd6824963c20617342e6d83b5723a46cdb8157c6343633b79ffed5fffb5e6315a514c5dc09b6a738ad8f1501cb5d101c788d356eb3466e7aa3059fb6d0154cacbf3190b9d397be6e8dd6f7e33e98e3a16d57bf23ece0aa0f90ace0d4eb12a7cf0405d014fadfb692b4fd21ea65b79d775bdab0f37cae4729c21c7efbdefe8b6e0f7c14f7eeae88df46a51ad5b8514e0b66334377f3d48ae8036581a802bff7c5ce67a1b8252e96d6506fe20bbc1ad413bf75b7d7785d1aa5648e81d3b3d4090d3b3ec423280b57eb6781010e3c10c9c9a0c5ecc8ff94cedf5ed315ab941a58d22e12214c1622e2e0f6374ef41711c161c0df647cd598234a46d252f65e1328d4fd09ca561d2f4739ffb5cb566e767823621b9946d1f5bcd03771404a1ed71041110c2e87835013d56c3153f75f52263bc3f65c363d77536224a27e531813a568db2510f4a457e160625631a8287152c8ad08ffbfffefbef3fbaefde7b6b6a1344f9ec78f8d9cf9ea8892d5d57f4e4da79c58c83fbad803cc3117c42d1e56597a7b7d0cbd81e7447be61663737cf2401e3a505dff8f0173fff457b972aade02e5094cccf8f5fb0f42a63d0c4a943feb72cb8ab1b256073ae5e5d7e4abebd71e97ca9cbb7fef0bb3f8c22481cbe3176340d62b78236ec27865beafc017be85da2964ede882bfd73bfd2ace7f5def53068a9a8e8a34b4cd38cd16e8a12bafe8eec41dc0d5ad56c17f60fab00af0e0bde278da0e9cd9842f957e70707c275647b63a83db8836c89542f42eebf964b14d3c29a430d5a1b3a1c8e98f9d3bcfe620042c4c3e79833eb149985420996441d2365bb3b66d7d3147853ac3f200d4c6bee086a7bc4280966e8120b63879773e2edd3bf7abafb9d98afdcc73c663433b4e0e77a297356c68b568598ff3281ad88f53e950ca3a4e74c19e38ace26c408a8dead00603428ed1a78618b46eae63cfedceda2792eebd28ee0df1d45839905b4233c3ca1bcaf4c79e5cb8a26f65a3d14cfd8acb0e18dbb5881b00ae3c5175fee3326a5343937d0f0ce78573b4716f8e80bb6f346eebc332b352c668ee7d5fc9c76f11e5fe8c9b51793da322a82675b4e5766a43ea647c61ad9be8f16f58cae172fce872699d414908d98b60cbd1873d08734ace384337aa956e993abdb6bb5471c22f067fd1873331bc1d2332fea1df276b2edc2a130892c4f6d6f970cac3869179cf269d2e19d0b41ec6294ceb11327324fd6cc1ba67d80e6845581ab0b2d961ba60848c0ea758b2c707c8cf36321b78707ea20e46603b011664a1c01b28691978b2665ca0921dbd17537641e2d668d5521c63ab7a6d18c7d301347028aafca6b608dfdc11cfe32e3914008d185364a08d127c448a35aeb665c66e0bc8ecc068b67ec6c5678fffbb7bfdd8da2e2fcd47bfd561cd8e058dd80d4b4f2f480b4ef300b973a217d2d9e300defc86a13b5350b379204fcae1ea55e85c3cb6950e5b44c377907b62908b8af150e60ac6f9da18d7794604044e08c59a6e70ba4d1fc017553bfff7c4b0594e363b5510f20cad88cc0111e6042818e012d0d1366bb905d1933361337d330701e65a45db5195e39778ec3634c62d688e3c65f7bcdf995d7b61d98b8afbe6aa7747645ff2ec726583cb0f5d6ea4e78083cfaa09dfa0a5dc4bcdd5340e8733942c6416381408fe993b884f57a7a3230ffdf833493f1afe543e7ce874a1d1e8edadb4a43b9dd7deeae2a7c3dbb4e48fa202617c2aa3053af4c959e6c9249bbba63b930a8eb04122f342a2f1664ab1808993183714100b7ac9b6ebcb966ce173248b68bd9b860ad922831036b60308de6c3793c6c9622d1a43df16ad02e7eab2ef0c0787fcc49be365ef2fe2da2eb8de0d195272118c16122cbbb7ead449ed55dafb20efa4417a6a50f36f8caa613b22c4f62961abbc209be15981206cc42db95bf2294854e43bf249a8cc5a18e9b985befbce3134ff99078ca156cb9e1747156637736040086573e4f1fe70005b5ca37be9aed2e3e86e8583c199c3fbf9dc71841b5f469052663a73ca44c523db2de65026c5116aef824e67134bbfd5d849fa365c167153cf9e493c589f774be9cf976cd60632ac38f42d0336df4575edb0ff8b6c5fe597d4a835c09207dfe762c1502ebe839ab7c501eccd586abbd0b73ab81cb61bc320fe3dc1fa6575730af4e33c70f880f261a789057fd7de24340855028f99324044bafe1045e15d38b6158f673b55e12c90fd91d1cf99a3d711a610b2b8ec9c34dac271acf9504ecef5c822707c74c9aaaa4c809f2af3bc261a5bc792b424bdbb77207e5cdf3e0c289a027639a305768d4404ffde653ae5cf32fc6e5aed1323ad8d725a9d4ad8d917a62e69ffce42747a76db24cc39ae4c5ccd62bb6b74acd57fa8e4b6a6ac27ca344c841f8fa25cab6aaf45386baeda885b1f2ac73e64060ce31536fbf5daf3165a02321c3d8bff9cd4bb22798b2f07de99c831185c084535f8caefe7aad19ef2469602be35ce611f5ba14084746eb0a527090b72109e9c215e0dca148e2618ad1f10373d21a4982543339ed773e9f107e29e3c6df9cb1d6126d0365ab033816542fc501a9a95fe8b495bff88b45c5d4a7608d9f3b21bdd58162b18976a640b2e0e02014bff686499c00fee2d5057bc5adbaafec57bf9f56596fe79a9e0cc03cf833656c170fa5d015051e664f758b90de44835acc6a6bc67b398ec011dea16a924b03bc42a602ae9eea73f1d0b0159e371d4fc4056f8ec918687033a792f9ac3001cd7af1621a5ba30ed8618c3c223ca6a389991f16ebb65192700832a5cddf621118ba775f5919278a319a86f5162ef5908531563d687ce3461a117cbb705760de9ec942d58b175ed8d1d33b0d6f93e4d063d5759f0443e805a4637a39ef449849f2991ed8e79da6a4a2aa1cc320c6937fc83c11a6c7ccaec60814956315302f3a6056bb0fe07cfaf4e92a0202ca6c75949a95278495521b864a0f9dbc1600f0089e38715760ccb869319cebc268d720c5fee029f465ed186bdb453d738304b4cc91eb6a9f194b1576c9243ea9105f2a8dd2b4782e5646da00cc6e794a1be989df493b72e0500c92cb6b1cccaaa00439841209d0c045c6a66c54f964eef6e5aee7433e3e14b0f57e776dc1f39431d946a02973abc4bcac4f227f0e01ef80ac9be4d3707a0e0cf96e5ce97a334c33c45984d9d56be0255f976815ce56f876af77348e300763107ba12b27a2b1730417a6b6e817435aceb50ae170b1620193385fd1e09ae7cfe1359c0cea7965182a68006f0cc0693b5fa824a4cc439e2b1da5c9653b023ef39987d288b7cc1eb0308a5ed496921ffce03f9b07a0d1ec331e5b8c11e82941298343e3951b02b5fcd4d76a0aa6de1d77ded179a95b6fcb81a1316fbefffdc75b8f9517acd51e2bceea92b733df44708a7b68274de4b5345cf3854cef859371a16567026f1dc788b1a9f78481b0af7298c7e69e8c69a4b92ebbbc579066a55b71ae87f1c307537ffbdd8ccb7cd3ac9f25a629872cbb9eb00e840ac1d06d6081ba125216d7749deb17bff8c5a393a74e763a81b79639f94ae60fb5fdefb21301be020716feb0b9949386223ac4bbc595b64dde3f8b562bc6f3cab3ae57a7112fae6937c1ad0b1fea25442aa15a1ab22b3544e6ff21f0be152fc85880567e8c90e14a0ccf3c1a87fae4dfa597559e5552e137aa384867f0ca1d4ce398cf39f7276bf6067142606995f10f82c28180d05adffbdef78e7efce39f54100900ed646e851220b80ad8304fb619f05f4cba1225e5be9701b21ec12a913733c8767622c632083758ffeffffdbf75f58606525fcc697e4caf6355ff223c9ca686296d2b10538895c64f7d8a57ae4ce32f7ce173d90df0a56ae435b9ff425cdea74f3f1786d91f455dc8c9238053fc53c8db11029f29a228c6635824d2db3a63de87383edac9df958fe2c1e40102ddd291a2440b78355d462e5d1e16fafaf28cb1662d83e43056e2ad539e3c3c8dcbe9d2bc81cb54418702df20fa9c94afb2589e75eeb7e792372f624910066d3674dad3cdfb890360b80668f7c683b7472939dfd1bca0784a4ecf6651c0b3cf9eaed5e3055163ee5a707dead4a90a192802fc57703bf027c6bbd5662b9d6761bd5bf12bce75cf6999ee5819bc80b8e28058052d40deaeb48771d2d78627643119514d03f24e3151d6e4b1fc0d2d40096ef6041cb41b953f73d88ab58018185606e8ecf94f6682cf5c4c192229cb22badc80b3339b170da131ca789a927b2314b1630ab66eb224be5f86615265ec62629893e3a74ffc242bea6d4eb47a3f33f681f5d96dfe8b0bb804ca1fc2461130c9982233e6824a90e97f5a3a0519908875f122a10d37b711d8a91fe6034b0f8ae9cc41d1fa1c3cd30bcde2e8e92d41313ecd9a48ded8ccc958e522acb6f3be265acc6e26a315161859bd055682ba6c8fbdaee906e51ba70266de92d2d3a6cf9d7eb64e0bbdac359a94921dccfffc4fffdcfd5b0b36f8a583bf296ef18e61059aa1b70da08ea06b5b45615e7f9dc37666bfdd4aaf2e037323562212d576e4a4e2adf579ab90a1ed2e9f096ecbd7ba1a258205014a96b385296c4c8ace0dabf2c15358e5f6e1aae7f5eeb08e2b8fb8152f9dfbd54e416dab045e688d86382ab2ca976165945ed801cc3b44aa1b3fde268cedc3808707eb4cdec9d7464d8506fe4638af102f91ca04fbfa68e013273e75f48d982e9899cd6d85c1cd190b69703d89b413b2ed9e672c1e47e3381fca2bb0829d9e23112563b7ab44901c5fd0837632b76343a45504fde2e7ebbfef6ee8a79e7aaacaa20b7bd3680e887923e7dd7fecb64c98ee34be73fcf311c32880a88b9d70a7065b93c18ea00d6dd56f85b2cac5d02ee3594a891382a38490a3176160fe705c981374b27299313de28d37a4978f023247e8f8020b996de2c458d2efc33436d7f96d118467c2c89c2aab67302d52f9cf354db80b1dcbe55b6f04573e655873a86d2d77fbe94f9f08ae3e6e1f81cd3f308deb4e5cccf7a6d356759defa0cdcde21765ebf14cc13805984080fdf1dbf39598c45bcff9e42f9fdc4c497c37f9d10b8c5a243c8679e1783de69f257778a2eff14e4cd1bf89e97dcb47fea6d329941525e3cb34f76635c6e1847457245d85ebd58f8bf75d5758f5f17c18bfdebb4ae35df793ad44892bf3972bb6bb954903af742b6e0142e835212d1d538e5b594f16efc796bc9cd67ba8563e20add08640d9c67fa210ca84e9a7b2997299a253b13d3768e432abf4614466826b1d0801e2bc11260501a4e59db188614c88dae2cefba81cf0e15f410c13181b193be0403d011c791d79c4d48d07abf320299f72b139f0fa1c6cb3e6f4864eeaa5a6658f1dedc4b61e8b08a1814139c78295237aeb4e4ba46cc2f5f1f46ebe8fcc2cfb588e6cb3d8d8a1344cc0ce0f4610b9d247f0d106030e594b9b3c531eb7a547246ccc60f8ad5ebe89932655295e70d35b3ff2d9cf66ffda979a0f4df540564dbc90ddd6cc44341667bca6ad9df181d9cd6d0a53471880ed28f5396cc9ea11bdbe7afc8ffff3ff6a5b98ae31dde02323bff8d9cfbb4994b733c00b07a906cf0dc7c45399d640826553a8a30af8022823ab4798b5be76fab5af7fad6337ab531c784461e351415bac8026c35f2b66ae57f3fc4ab3e2ffbf3cefcd459c2d68a1f7090ba8578b7ddc3779f242dc18c90a724c8319eb8a967ec1de724e4953ce14b7117357f454b82732d5cba59c611ed6751924b068c505bb6bf5a2dd694813957a2a82c4dc62ce101c660a4663fe2c4213a48edfc00fae84d259f5e672acec562e86c2e404815d8fa92a7469e03367ced4ce5fa681f4bbea22d016c4e5d5ae65e13db84f4f6050de3d6d29ebfad050e01c3879f264f1ae9995fac1ab4bce2886c098b35486360b87402ede05927bb4d1f3d1e804191250596dbada037ec52970f50ccc670b9887f611d6281e3d3e534bcf252daa31d1f528181bb34fbd62be0520a1b2accbd999c67404d532b093274f1dfdfddf3f98552477b42de04e299a52e0cd352e16c0d2db1853adfa693b3833fd79909f79fae95c67c29a43c7cfa2f0871f79a49f7cd2ee0df0058b729eca16cebcfceb7f17add675a506e6305cfd5e5b8cda49aa127a1179cbb9002ca22d600bae3cd35c438caea5a3c92e5bc378f8218a69c6bc488e957ba0a5de5b9878cf5bfdf950d250b374a638446b4aee3dc13017477bf5eb2d311169bf071e78e0e896bffdeaf454b6e76f5a584f7528949d43291cc54730c2d7cae096f7c54dbddf7266684cd306cef633896cdecb98c4cee63fc5abc7dc3b34914ab7f4cad10113b6723868d4523918c5bd60fc03f73703d742612657b7136dca82c258736a70d590ea7f215e4504d190fdeb7e11b4658a77ec5bc68e511c98d7ca0ae10a66489e81d157f5505228c6a3d67f8202be0d9126f7f5a0846c054eaef6c4317709a65e157c74b3da9e17d364f31a2f1326c2c8b174db7bb7b5e7461be51883538ea79f7b6e14b578f86d345326d89e098ba5568f3efa9d8e87093905ef3d33d43230534ada66d577eaa23605b46b8f1dddc427244bca98fb799e32578cf40373e217fcf57e770da02b8f1f58400360ddae0276990e6fa4dbd262ff31ebb20f343633ef9381f242a4d900c559b9ba9d8a89d822ddb572fbd2035eca08941515ef96a1f52a7a1413bd360edadec06c705ec457bef295a353f79caa29683804346debef6830e58e53847d0f3e1c0933f73766c7e8cb6133efe668338cf2cb5ffea23d5b57d3072673a99a75a3056ce5a912d8c60e535b53017384811effbd4e75ccca755554ae7928ae72025e02257e353e8563127ce2d567deb5474ed9686e09985e57eb8df908723469989e90e9d5afbfeef93062e63013e0b8e0376222ebeed6ab9a127016e43201f5a626e5ad153545a29ef2a31b21e385b5e44dd902c530f7f9ae41f841db0e5ec7da7eb6347164dd9895f12162be5d604ef2c33d14e757bf7aa68e2ca74a3bf2cf98fccd94f1d4530e268ab73969f994dc53b29a587dd08709ef5eef062fcaa5740a4e708608bc7327f92ef49db8b6e58a9642ca7d685e70b6b0f25dfdbcca684fb6887598680172dd85abb1ca8ba0dbd718d7f84583da2088f07e4d912483c89656ec0ec90dbe4b7efbaa8b2058b36bd7279034ba835d78c998046c6f4c4993128a4b31fd4c28f38ab567e90a9f20bdd561702458a385d6d89106b4da8189f2d2cb2fc6043cdb72967912448249ccc6e0b26875ad559fa5f3281af196ff60f6969d670d6b30cf54ed768f382aece036cf6770ff625661c8078cded037d0281073710bc745a6f6b4a1ce62163831c72d7e751a96b196f57d761318f49ff8e427a3cdc3bc0994090fe4dfa4473406ed21a0689263b6d166b58532e18e2e266ded96d686844b503673db7480b869df90213d8a5ecf7406a657c62e7d7a2c8b8e5914e7a358c0d0b2c6750b3eeb8185c08cf4d51969fffeefffae8e1c0e1da62e0febb3cf3ed33d69efbce2cba1d3aeda341528ccac0ac6406d6e6df766ac1bcaf7aebb4ca01ff814d41982887b55fd45afb0e8e2f9500e0ee357dabf740df8bd0b7f9f110926ac86f6d4fb416d7bbbd28c5618377ebc8c21066d45a3da5fa60122757b24951a380b761f874457c095064e18ff7b8f7d2f1fe2fe591bd000da8fa642383fc73eeb251673103c03dd0dc3b944006727aecfbbcec70199322ff958433f0e38ab013aa80f93a5c65b7e9731fd0c95a6398731bb2c283182d4703089cde368dec8c4f21dd9f2604cd8f160180e7db8ae7f97e98197b2da7d050cab77e66124ac8e461bda6ccc91fafa16d9a54b94d77c8554cfe14316bfc9c7d1994d6fbe319f967de4e1876bd661d6b64b70bc29781174632d822cc0bc262c664b682f932ba78e45bbcc61b494475047ce035e4c5e508a413bfad9186aa57c69bfb9c7eb49dc84cc58773986c0d2069639d95e235840d0d5f58141293999ebe4c99315d880af15e218041ee19eaf18bed2f60d417fda223826aa38055bd68123bc1fb8fffe9ae093387f014c9d5b7fd7fcf6fca8965eefe33caff7ee85f5feea7bcfd2eec336263b0430ef0f13ed93bb2bf060d73c928579f324a295d6507a158cd06d2f11b663d7ce89b8f227c35cf2778f8cb855e630f07ae7142c8375a605b2acd508c78e6d308bc310d9ba3426974962786a64c13de67d211f7e7826da9060fd3627df9a33792f732c790dfde2d3fae78ff2fd836f782e8c3f8d51801d6f8df74f4fc53b46901cb4f3899c8568f0ce394043335d9887cc4a30f514edd52284c65ec610e229085a9797eed2c593517ff90e75cc3ade328ccb818089f5e4e348f84d27ca312b8566f50c87cf0d71eabc9a55eb26ece1d46fbda51ec7339efa507a04caa74bd5542479aaa4d26668a4a7ee4032f470ee0927855e7e995b5a89a7d1ce0771757e24ee72cad53b7a466782002e987a3c5e5fe6eaec8503c54e80778f9e7efaa91c7874bad60767d2b4d9b5f916dc9d5538abfd3a7e4e3bd897e64ba7c78e3dd53a73fc944f722d1b029c32091c85685e8cf0b278aa7493cefbfea4dd42a705c427c04198a473ef79c5bb175aeedcfed9dff5aec393e0bd998bfb7480ad445703966ad83685eeb3344e1e8c44c868741ab2c7118489f434cddbcc2be730f00ecc56b9dd7333c4c3168d39aeea68c2301d4682979f81754db3a4b58e4dd9e23b891a0c0989d2c471f3fee8273f3afac1e38fa7812c2f1aa61fd7fd68441855f36b34665feab07eeac70c820b73ce18e1b630b139aadbe25a9f5dc23325c0052f7d8a2d1d50abf8069ef81bae9f3a11bef361620c8961089315269c13eaa017e7917bf5d5f9b8a13128a6c73c26ded778b0f9a35002a6f8524a7ecc4965a4f4d28632200076145b99af4722f04c31df067bfa574f07cf194719979ae805a7bd5deb33708c9b4c35bcf0fcf3a53fef5f972d8599bb6c29a77ea1b1bc04efd7bf7e21ca22ab4bb6b670a5186c09aa82089df08c6572942865a9073a77eec128ab59fe846e7a54531a4cd6ee2a00288da69d5b7704179130f49e7d8286191f8b45612823be21f056e8dd56bf891b38d386935e3ecfff7fc24a3fc6765971b2af170be8e1558a5d35568552eed2221df3d0dcf9c1a7bd597a3423d48570f19447fe0df1f56ec3a0797b9f2437d0c0618edaedd1eae95476819644785ad5798d8e78beebc4897ab8aa49115c3794404098386b558a1e46b9b41e416ad8b90383725acd2261798c7130a783644c1198d834e9cd83658d2513b0e5658ca35e9622e5efc054cfa08170f99f5b5ebafd413f14916555f0770aae39b8ef7ce73bf9dcec996a7367453225093d7ce18511c7b40be0d0b6b45462d248374ea1d9d4685e68e585f797bff4c59edccb24f3d3131380c7bfff78269a9f2dac698fecb14b6f688c4410d04d398441cf649e8bd7520f7c998e0a9df5189c434ed5d20bbe1a65d139ab98a7268ed51f9d840e23720577a66444e6972a99ff22184c777376e599bcd2869d748f23a4bb33f2027927a3ac6891ab76adf991f9cd379c899f0f029e3c79743c756df266c9dd64f634b46d41036392ec52f7fd8a035f183a1df276daa738edf349b709d964102120e661e269c47dbc2206ccfcf58c790819e69dad09b61b3019d78e5c30f7886dd450dcaebc3e6ccfdd2691f498c45915189d666632102a9e32e7e26b6c3d8b8338f510046f7aa7810b26fc2cc0f5f9242ee93fc64c14ab5efe414c9aa6ece39cbff1e0a73f9de3043e5b01c08ce6ad3a4db18d314b23d902a71a791332f1fd959ff6e5284579703cf1c91347ffedbffe57b98f3e7adbf428b6e49c3d7ba6f33ea60be0a36785dcf48ed3439676f316f685e986e0314129244c666ce7eda23b867de0d30f363b41852393cc8e052be3298e59819f6cc9e79e9389d9caa2300e3e7f3e63c308963196315fb7f0948e39572363cac71f7f2ce9428f94dd698896911e0412a06ebd792b95e73d0dfb5a353a76d60312543ded124cfcc5ec334dc069d432c09429e02993fea3f5539ee5690eaeb5589843cc78527ccd4c6d91fa344f014cf9f3d7bb81bba17df87277bfe442847a0887718dc89f9d90fdd9cbade012e1e0be80c02b16b9badf7e90c70cd54e6944db5dcc9731eb967926ff156187dcc41e564a1118d22a8daffceddf1e5dccf8c97887a9d69e24abf4697e0c8378ba54f540730474e31e5e84d5aa0f8c86799a6eabd7a41a41535fb862202b2b4ede9d15de318f5a47b0c1cc4faf312b54a668093043c711796feecb9e26f3780485c9ccbc53964da40e96a12894d5315bf2c2bcfbbf628ad1da357b9377da26f55bc4695d933abd98de840260bee96dd7f7d8cc11e96557bbbaaa9b4ffc86228dd7839b1a3186e62aa7bccc0d521882f28d857ff7fb6c19ca8650de4b5eddd762c2be1025a0c759cc253d9a57d96c65cddce408b37668ef957768b3cc7074ddf5ccc14f1a368031a589669b2c590c0b7fca52fb1fcf2ef90b6fcfae02746beb85561d83a68ca97756d344c91bebea518d25c59f8bf2d77b5f8cf200db8a98c37aa88bb0c8bdca9ed8fddfbf14bf4f31773b215b2f200be91506d9f574e5759f0a8be5974a6a30e6a2b193791b0db1ffa4d23068ea29f510a6393d0fb479a78208350df0f1f456c60f08612293e0812fcf686c33f803429a1957e57d607861598f31031c2d7f9266572f9153746ef6c140fe9d1c5c338e05cd3e4caa9c96a19ce495bd38a497bd78d109ca17ea46377e3261cd6181e1efbfef819ab6c533b0aa8cae09f903803e98c9f1cbe92df3adb5b8da35bef197b22648d8c277f8a3a09ef9739fff5c7b5c93b837dd389f4e22b872b6bcd2c17d685153762adcf74943f8396aac853c73e63735679709ce4bfa8b5ffcfce885177e5da6375e322624800465e1d75e2160b5fd623e0a0402c6cdde534c2be87599e20e8ca5044d3db054a490bf2efe08b5b2ec24109082f263f63a6cd666d2a6dff22066cb09dfad92d008ce7a33f832a59d42cc1b2bd143f1c43e929521e01ed64779fb7aa0d484555f4f87f7dbeb5e76f1adf7553d9997899fccbb16dab2cf8b1d2cc8174b7ff5209e93df98a14260501f4d61ccd1f312b3a8555079e52cf0725e1dd652a1a64e5a0da227924bc505a6426fe19b32f5201acb7bef8c634c523aef8f57eeb7d1bea74f9f0ea3f8c83b810990ad0a838d7a0c6c0d45037296f096315938564a9fe08b19686c6570134b435b7258d4159d41bbedfd0454cfe94cc44fe4b0194c35145083114e9568f99b89a307f3051b4c4fc806c9266ff973b7d1b074bcdce5527a7b2b5508ecbe1eee23085ba6d2bd79f6746cf989bb25735f5ffa62ceda3f7177cd315b4566ade39b478f7dffb17e48833344dbae00ef45bb8527122e6fe094979e3aed47502ccbb2d9d51a4c8b7599fae6f26ce979f43b8f7632bb8ead40e504d293719eec9645259e3050b826c57dce69f59ce84ac1e3939b62f6fae88676b36ccb385a5bfeebfffed7c0cc46ced004bf58286cbae5befbee2ddc55af7585ff0a687af8bce20fafeb7de98f2f931d841dc5760916d3e3b7e1b949991cabf1565a05340eb00d1fef0c8eeb084865eac64fafd0d68ff7a8c9c06d9e550038a0a5c91a2fd556dea685ab65fa3eef92761c00d39b69540c6fde860dcea43107e6d866fbcc3c5bc2e37d9581c295b0999770567ecb4ebc3a197798e0c5e8cabe9815f34c0c734c56118039a693ef376783604d92ec73e3a4d81ac4f8944962a2f69de4bb1c73a5da7d232c2c4641ede9c089c2f367799115508be6f095aaabc68758a232899bf93593d811766b13d72b75aae99ae3edd44b3c86b4bb1ccc5a1b61fed59684faa16874e61a13544f2823930f431390b72f39e33fc380a4119a3770873ed3b36977f1755264054e3d98392e1ca33b04c95a48caa06b302310c67494913a9bf79ba619fa1b97891b27cbf4fa7063beeb619f3bfd7cf0e488b10d2a6b546346eacd9565dc46386d7781cf534f3dd5096fed60cc7a4d7677e399dfa547e344b1bf6dd162b55f2bb9fd59ef0ee3dcafb487edd4341bcdd5a742b61268f409fbbb8d1f1bada0959649a9d1dba892e7e77dc72569088d21a848c7654918e5b10bf20de2a0acfbf57ae2fab4a56b3929c4b5e65fc667189a29a021f42206e94cb4dae0b1e9cd31612ca14507bf412140dafb1e94332cdcbf0a610e19bb992035f7648ec7a4ef2ce18ad0e65d976085496a3ab5c7985eb73dabb15634ab3aca2bfd1dc1c5ae6275183ac266fd6019e64f1e9b2e310ecd0cff2aacbca381a59f3a0c8c7195bfd15dbfc678d2aa84b426dfe5c7c41486deb51f790f73df932d1f6b3e2b28d6642478c522082e41aae3294e2763606b3657fb4fdb059b346a5abb3d815e01cc3be385e54831794ca88c190f57c037ef46037cc27b6c9ce5fb62e0335f95efe8058a0c0fc1c33b7999c89c52ac843ac122501401456131000157975132a3040821eff0eb31414ba35091195b85991ed158162eab7eebbaeab9e8e27a18b7ee5dff529edd2afc5d6250b6408c687b61446a58a2cf21d26a882dc1b04b0a836c0fd6c9bd9e4c23138c393330a90372cf28909edfc0c54c85b8556690e73c59df1a73e213e22fd3cc3d17f13203d7dc591920b8808f000beedc4f218b388b40533273346bdf32a9fbad6f7fabe6a10d826b39d272600c7d8c4352dfcb6b494ff05db54bb9762ecf3619c720ec8f54230813dc6c0d945b5f6df96098c7de389ec277526fb8a98350dce7a6f77a565e388ac5988a7039049599c569619e6dada67026a4f1a3b59d1f8e494a28e1ae75e1d0f6cc2dbaad80b9b9fa31a99516c565ab1ff3d711de0e49bd33d30f26bf79ff9cbb016e156e7a0cd7456765b40e5b637847c8f4564f5d6f11717a5ad64b8245d91403d391a04e3b1e55b89cf7f8d043f9a20adae7b7b76c8646ca719689009aefdd29e3574ffeaacae7baeb4659a1d36fce9ec9b4c6bd9b253082dc7ce599c1c5f39211f8afb0eae5baeed7bb5e93f68afd6457bcccc3022a1e4a031bb362af2073d018f34e414cc539ec1401393d986934eaf19c8d35907a69ed0f994739c2c4ad8656deccc1fcec673fcfae6527dcbe1613ecad12bf8d9e7268ada9a81e44defc5c52245c2b78ee8be8f6a275ba92a853bebacea2d677de7eab42a6aeab879ab2a6474ac278c352424c566335650e730df32a0f93684ce33ccc22d1012514b90bea70439c1e34b4f1c79cb2e5f5d48fb2c2406ad0f1473c6398ddf8e43fdffacf4e5c3b7a8ed031b798477a34d536deb193faedf4ac4c5826f1f410810ea090ebd0481ccb64ceafb428d830403ba2836025fd030f3ed031a7d5f94b98bc43bf0a6deed7b53cb3d563d284f6b9e97eb728082629478716839065796b02def86af871da0b6de619a4c17995d3fc22e1af62f9b10e3e91f1d94772e02dcb67f101ded4bb53a2d336034bf6c37058d6ba3fa4d3615af7dea5c58a63ed03991a09a1840e83e6b6cffe68a4bec61d0987638b5558620bb442c60318bb1ea134e61040e60d30807f25286be06a9a5922e3f3b096462dc6bb261accd1001ab760037352abde04795b546ea60ef3a6cd5b1492a3d72dfdf6b0f2a98b1eb884dda13e19e0177eafe62584d767bc00977ad3ccd924991eb14216af16f392e770d1694adcff6d7d5371e60e2163fef8885f2793ab381c8c33672332c94c3130cf984d3c66dffdee777b6c02a15a95a2e54d734c838f02c0c8e0724aad7589ab0d5d0f998842313739ce9fedbb026a103c095c97856d6db0dcef43c2a1917694f610e6bec6ee72bc41e8661e948b9d00d4224993aac78ccb7edf256be37419ba2e815ab017fe87b0e1f35e7ef88f97571e8e166db470540767e62b476f3def16ee7bb958f00febb1ee0fcbbce21ebfc412dc998b872f5988edb1739de2bc9dcab5b1762c3cf1fd0b6032790fd1b18963e3aa6834781d0271aee5d556418477af51a72156450a6f2b1831540671acc6a6f5986df2e48d3f0761b013116c0bbbf9570ac927637202acfc0d86f2bc4b86ade86ac1dee78fd8183d12f53f90b42941809789715f7191f9c9279feacf1406dc694ba6da72a260fcc3ba4a83662bac152126e19d35828600a3a915e57ffff7dfe821afe68c301e6bc1ea1a4a4dda9e7f1298fdb7814507f57331f9dc4d96e9e50cf80fc3fb318ec5c59c0acc3a0a6ee16e82d75172dce2deadbc2906197701231b57cdb0e1529508e1965e3a8accb2b43ba2347c7c9e90457506df5973ca33fc40f22fcfe6e4db17501e4a6962dc2b0b4e4c4df5b4def3b538c098ce84f83028cbe20463fabbee3ab1533a2bcdaa93e755ee8a6bb92abb85c3e795c6ab19e926617baf20b85e166129366aad78512b6eee87e9a6a8ed6f4c469e353f35ef1ac68c47fad04cfe10822b116c6cd22b0bc22b48c5243236f01d69bb88cb405754105a07f8e77e6060ac024d9e1168f187f0e151b342818169dca2f758d7511ac65c1bbe016705c403d9defe852f7cbeabed091b8f59773807bfa79f7ea68dcdb4a241e7f8eed1a68b5946b0c08ca3258cb8f35e46ab5a04cc63d8b7e801b5fc987b189a876d27ac79f1c1384a6c413973e66c53a2a1f42bb4ee7910676cd88ffec564e489036785d5ce431f748a991941268c7a33ccb8c64c9407a6b5c6d1e268f55974652eeab931b9b2b8ce99b0e0715a7ce6339fb9c299d1d5fd1923325f096344af69cd971164bb02a6d79d5a2d3c17dee8d30cb930cd7ff8c31f1efde4473f6a997608e8cdd493c9bbf8ae79b44d1c3ae6cd581c4bf807dc9e9fd6b3eb0a57f3e98a3fbce2bd113235c7402d759294311b7dd8547987417321942de4f0b57b8d9bdfacfc30205d7bcbc675ac28e16a22891b3486a15a06785b7a0d68e994f3349a37656caf644d1882ec1b59dc2057587dda03ec9c9a5c1b4204a1f0fa0c56ce7e7452318d9b3ad8eba4a116de98c140fa9e7beea999c32c106efac0850ac18ca730cbb8ce6d82c46c18d39c1ba1f263ba995e30ce7c29bb037c73ccde398cf227ce9c0d3f427ff11a4e9439a189ebfe86eb4383bc871321efb825755617aef755b78a1b7a8195ff29b95eb531cdf687a1c25539c896e449df980a0fc1669afe2a9b26c15f828941399ecac4ed49071fbdc973a74f1f7ddb370462e61338c20d8e76bcf7de7b2b5045280532917918b9dd798805f542231ec03f86769c418bfe4d70f067d121c8b59ece1b719a16fc46b8c611a372163a08f2a82ee132ddf3667a3f66fa28bf29ffa0883fbb5d65baaefb057725866f856c21beaecd5064923448e01fefc4e7461bcc8b5e10b5115bba2489403001540ec2177328e979a70a87002a38b006c62eefb4e8d03c254ccf3202ad5c70f4621a498134e58c0ba7ec44ae9b2baef2fa07ebae9f33868aab628d9b16436a64cb90cc35e9312d36b6cbd7bac817c3fcdf7df4bb1182975b27f89b9b232098692d0856865dc48e3be3ce66a65ca63d833b6d4aabf718b4382f1cc26a7ecd0ee39733f5c0bcc1a80d7860a3353aae76812ba6211c966ce939db56a9ba413b37b679b6f9e8dfbee1878a7bfab8eb7eb130ae32298481131aa1551a7ca1001fe513628b98d149bd2d0ad676e7d2c3e8a530ea82d33cf9634919463757c9db6c7a82a07956dfc39e8929cc64bcedb6783123647b419e05cf6864f1f794c17cded7a7e59527863f99d1e6d12c6476380f5e5a34c437480bbe1f6e300d62c99871aad5392bed157095a8c27f25a0a1b0d295f712373d595e1c2690a8e66373f8b37f3f4ff3b7407611f24c65544a4fc62c72a6a01db1c6659636994c14e0bbba6d15dd05f5d81ee1bce15d21e37d72c2942efffcf9ac0cdf76c7cabbf05fd7c338f0668508c69bcd7e3d4b304c4a73727d3b968ca6e6d62664cae90a0db4484f268e90818f3ec6987aa7377354dcad990fa250c47314981bba336334bd91d51b1adc4959e03df6d8f77aac9a81b65e8da626f0ca3096eadc5a8472873f5a6c6d1b4c6a7a5b88ab67000f7373e3f390f949ba1ab940b63f759ea0799e9557f778967e8183d957bdfe3cef146e3a010d30eedbeffcf6e8d846074a86194868d072f5027a520e19bd9355f0a63d280c533194841fbaac1e513ee9eda23e7e3c470f70de24508f705486ba12f2145dfe6982833f0b776998ced681b20a08d3e28b554f637c2b5acc474afb898c75d56ff872da78810657be056395e3fd8a5b6957dc2e4dbaca0ad902b07b01e06ad983dcd2edd2884fe19012e4d85ab83d997466e3113143d16a7c8e80e8cc1d8cc95146c9970000400049444154abd9f78c014e6336f3cbbb21aa45b6c7d3108e5fcbeaefaca4d8152e51c2aaf0d4a7318d2f2ec1c34c3f73c031cdfdfe7184cac25a8e0b3d012d691ca9c195098e71997572870c0428e130c16cceeedd77ef6c3d7d03002358af69ccf1f0239fad29c9816192f4e59c29783a8783125681d9682da61e79e89aba2b374ce1b9f508358ea5dbc0a428c39bf74a06f0bf7cf297154a26e64b2fbd52938a50eb2984a1017843df699cbec94b2b45d2b31a4f45513033a7cea96fca5637cf4b0064f0dc3d68b7ee0f49ed76ff805cbd2b9a52acca947799984f3ffd6c7b8bf46da99b8f0afa7ec0f4a2937eea4b69f03032d1d5a5b8079fb5eb9a8710cca1d5d4518d0e9fdd1b43ce2a908f1e3d7fecb9c07112d6f56d6307e31a87dab5dede2ef0c054b60da914c16180c382efeab9781d263ac0e1aae8f2e8ae275b80ae00aac157ae5191f324722b702d4d1a019bd7ad6890e5fa3ec6f99197bee9cb01c21dbc903d803e19b7d21485a1b6ffb2b76234d42d2192d5f476eeee70967bab3cfc310481a2a16fc907db11936019439930b54b9740d1b698412f923e28bf2122136a089998bc6342eaed5c31488aa8f0bdfec73fe454a567ebddeb3791b322840968bcf6852f7ee9e8f3993065662db3e0ed8fe4fc8d68f69ba228f4843b737783a7b7194f6286fdc10b03aa6317ce327342079ec4679f79e6e8f433293778120869fa0bae6835e41866181a89f43c66d2a21146b7a2a5db8862c2da0829ce625d3dfac99327cb7c3c7094aeef97d90541795ae59262734cc89caba2c7300fb884cc3b632f1f06b45a04dd2848719c24ed3d610529944ffa1b6eb22ed121b6d945fde69c0fa92eed2d3785308e9a615b79ae0ee0a9b30daaf7dd775fe05faa6235dda197a464958d97daf649ab1de42b2e9eaf023a382e5ce73a740de649bfdecbe6feeab89d901dc29d6a2783d23c6c61155e92330d133fe407dc83b8fc832806a6a931719ed9e7fdda4bb5f4e6575e70f35e288c42d4a4899b02b6789a3f3d598454378ff9578524f0aee3929808ec7aa61f130db3989b720214e6376629518387fc453b4ca4c782c6fca60eb49af7ea6155bc8679338d7fec58f2269e89f4a378b07ef293397f9f379090389bf0e4a97b2a00ad57c66fcab2c29e49a2677c434f9c505a19df44c88fe5dcff0f6d2696357804546ff36c04eacd68f1a450d54c6158ca653d28f7f7f438e0a097f75e4ccdf4b853f66c37b28b6168a8c730e5e003848f7ef73b1d27bd1607064f21985ffaf297ab9428a31568fb9eb41c5a58c572393d2c0560b915736e4c4f93edc22ce6bd3b5b55bef18dbf2bbdb44985288c6e6588fd79bb10dc675d623693661cacb76f9d92c05ce35b29af477247b8a543cf56b89073b7e3a112200ea80f7485bd034eb51da58b47d66a1d7997f7789448281698e5cc0dd60eb7ab6e5659a2078f7d82ddbb41a3efdf57c852dfbedc31fa1e46efbc4f82a9e3ee1dc1d0bcfe866582684daf4dc830c68c41d6716b2be3864df3ad387012bfbd52e716971b5ae80361448c504741d218ff69bccf3ef2f0d1830f3e50f367b4d59c7ceb7dc105485788eb15721f31dd4c5be69832f23e0d3aabece77c0d668b035f5e78e1f99a82834b0a4d7a8ae38d37b25f2e79c037317e6dcd3f3b72e7080082822232180732b9280370350878342a65f0c0030f1edd7bcf3db33c29f9647470272749997a238833ecafc5a01b7d24449ff9934b95c6bc5cbd3a0dae17707e0666d7bbdb66f2eb332f1c7deb9bdfea4e6cca07ade0a72731d663262fc6217084cc4e6eeb0ad14bdddf8d79ac07643e1bdf28d33bc2e0d42c0b83295d4a8a6388636ce52d654204cf0480125d93c2eda593362f2bc03eed649a031ed20f5e435dcf87b096b93a34ded2045763f38e819b7a4f42e9e0ddb0c1da92f4e2fd2a43c4befcc354132fa6b8852d94bc13b243203b6083db0eca0e30ce4858da52f508649febdbcf3373318435eee00ca049cd7f100c87644ef1a0402c4f0139607393e715a7ccb94f098179637a24e607ad645c9676a940132acc73e79d9fa8593790e7afb25ba7143026da34880211fc42c65270a3897b145abef365e272c63b2f47dbcfba43388f265cd88f901c8bf9a4fe10075f1d9dd561bc83b1e12ab8625a0ccfac5c95447be322f347e6deacfd53023898d699f4474716cf8e47b50d384d30f5423075cb3f65e8b5098245bab694182b31772dacc5a0d260424c6c9c43a80817a74b4da894a697f63109f8b3002aac1112a63a13bc1faf9816dfd5d7962293e58742d01dd4e9c1378a07b26a6bd3895957b45386b11445a03dd1efdab6192fe63ba1d9cb353bd1a33408acc5b7e0acfbbe0bf8c3725af0f6a7a42bdcadd5e01301bc901f5e29df863e681a20bbb2ae809177abccf78b6f1c990d1e3b21db57769f650179df7772974e4122ffae087954510d56132dc43b1781581ec6492b4f01f471a3f91617881bc8750dc434c251ddecb681582ae454aa158c7108ca98476297700ead26ddc273343037b6c36a1c90ea70192ef77e81328d4b8b73933bf454fd0917e66c0316d8c0d708a54f04c0ba42f7c610e6bef4567a3bee69f98c51adf7a32430543535da258fc1bd31cd6018e14f83ab33b38750820156698628219dcbd07efe1214c2faf0230f1d3dfcd0c31d8328cfd444856723e6c056ece598a4f3b51cbd0cb3099e7a7287dce8cd08ba745689607acbb74c106f2440e55d7a8a45dd099950bab8cec35c73bf68b87a0ecfd2a287dff198cddd8e8218895fe6fd9b7fcce7ac22f8b7e7501c79e559b01421f47963ab669fe82ae2bcec7bf168809e78c67ec03ab1523f6d6eb1b371a3f69667d503a875bfca7d3f1cb622b74b14df95117b022c60de6b42e333c83568e0dcac311b424f334fdaaedc4f02631a26832f4c5e8ebb5aa5cc9921dc1e18881b5572b7085e488121e90adeb1e399604c1dae6cef25e1d13379cbfbe679f0d9675e4491c1d481f9130e84c71e7b2c5fc63cdb05a25d651fab5c5a3f8ce99f3071f0c1fcae23a8a593ba26bd1e9a39a8e7b8efdefb7a251c2b18479ac3d1a358afb7735307962d3b18c82a7470d46b9c2e71de648c82e946c8d46d1fda364d9c583887d6a610b8a4397856bd31d5619b4e16f35ff3792642a66c30a4a5245ef8f5f381774d7bb57e54312b2fcc61594cabcd51be74083d67370037fb95fbdabc2fed22140befe649196574ca316dc131c22cb6d2e5f9e79eebf395353dd6722d843e75cf7e6eefb04e3baaac829498f2a5e1d97e2fbf2ae32a8c2c00485d4c941bfff99888ded2dce3df7efdab35df7510cdbf038c3c237460fa2dfa1edeafe4e2843d076c24582ff609dd05d892288594c0e235aceb3e68f412d5303d8435461964f331f2d8d3dde31546cd1014fe49bbcf3bc006b169c0d6714b90f8e040bbf0f29933abe9b755165ce320837093b2a3b9c13fd044a5f92804f13c75cc223fc13613f904b82d5ac08819d19017985f9d3802b87f9d0a6c3fd25dd17e8e4ae0dc60ee503053fec0c266e2098d35983d5eba65cd573e7b38681ada44f622ad41bb69068a05238227b4515722115bdd6af2860ee871f983a308bc5e61f51ef2fb318dcc4f117aab5ad4d58f89fce8a3df39faf7ef7c3b1f47dc4f0b287f681bb3a2b8e00d5302f38515bddf8ccb962b7c63f2f488a61766e918b37cd616fa281f4b82f0be9ecf5619b7ab8c7266b7c3dcc3cae6ca975f79a9c246291c86a17370d9e823bdfa195258c96f11c0cb29cb9993afe5cc4d93cf6865f7be312cbaf89dffc0f93881e6638735a3036fc1044f58cf5797ef79a5595771074206c0003c4c30382f493860d8a606621aa5c54b1c4402a58818e0f22e9a0b52015ac442d68e91d2cb0db2600ff2ae5b3d0af7f00f9c80c71496567d303f2b299817de5dc884b7cd916f672f150d398cb0c71bac055b69849580d8a2f17234b43cf0068bd9e4d92f18d554640221bac958db3e9cff67bc72dbc7e62b2433796d9e2d8215bc84056796f10c2e3d1c343d8d3d6396525db8805e42964b6d66ea47e11218f085e78ce33e3ce3b8a6dd37f4d02510421cf79894c7d058cba2dea534d05a7d9873e6f208acf28cad9e3bfd5cf3b5ee81011f9e37ceaa4517b4417b7028376d213d06f5201dcfab71dc5db91794839195c531f2eaab59fd1161e2897488ab437bba11b4e98d3709f0307b5a3a1086320841c7ab9b09774ad15aca8e8ff3aec8480da9ab82dee9a9a79eccf2ae7f6ff997b2d9b71f31519fd098c234a65e42c65ad05b5a68c019d5b60c5c90c157e715d6f361dc7ae72a8fd40742265ad49f23da3707c03db742925f9d25d9dbe9e5dac6484534b44a18b3d8feae41262c34b647e052cea2551bb144cc8b2dd2e01c8332859846844c005b831aa78933663bac3cd629aadb1f7bce3a1d905e5195f73811f4e9312d0236d0f791400e04e75ee8a53ecc431821776e851e8db00b50e548b91cad0d281aa9610bc80d7c088d31166fe8d9ec04e652b70cabeb20efbda7bdd82e5ff28fc76d76008b87275a0aab7eaedea5e43062ce2689030293bc77c72c02c09c18de894df69a9d3d73b66612c6371e61ea197b15864ac03bbf3a42328e9c802e5e4d3925a617add7e0628fdad9b367d24bdf5885f762e60df51ed66cbe9d7d794b09b6cd747f0969864c5dcc98156cbf15d4cbafdc92038050931b9f705af676656f36f0e48567bbd75c2806263b1a50ee8438dcd8725ad6aacfd652c6a34ea1a6043e75f1ee7612c503c80d3fcfc27aee43feacf8f5ec0ae745c1c6bf5f22710bd8ba775d69f724492105baff231f01c358c75339264c0fd64983de7003add062037faedbd3f630d0d6bb2d6918ccb8e6a68e25b883c15330d73bed4dcb2128534e1585e2da7562d87062e1369f81cd8703637ac0d1da48736a9649ddbe9de9b84c3502a52ed5b49b26df2085f1a7f7535669a5dca4816b6915dca6ae2334e6c0be9843464f9d3a556d69829c3384694868c19830b4671e1b67d1bac371aa3c14e95c4f1850913cb98e9ea3dc3871de7bef89f61add199db107c6b1bbdce05ef65d1ba6bc55e6c2bfcaa2450c0e2d602bb3750a826b4d2021b003bc736e8f3e7af41fdffb8fb4c57c526a2941b45bb4a819c892d96ab92ef35ecdf20fdfe51f67ce32cd2937ca6e792f27cdcaed0ae10d6a357d762d44d952687a7502eafdae5c3408415b77917d8e4d91cf6159a87dfe33efd669943783fbc6b08b56e2dd6b7f990fe35b177508dc0a59913d20b4cc87e1f0bdfb3f0bbba8ade9f30ce712295a1781112b98a681b35eaf9a1e820bd2eea61153c60e6821ad942ad3e555994f31b94b3b22d4a51c2caa27e3d163924a37586c39e1b40accbd3620ac777feaee9a3c4c416e6e2b1ab8bff5621a88f0a9079cfa0bdce207065a6c745bbd8b321483b9ce9d1bcf5cd72462b262e4f8b50fc7fbf750e15e178706daa00070530e9ca54e0830c2c76b085ff56bd85ea381ded6bc17f3c667618d8968fb7ff9977fc902e4974a73e34f42aa2d0821a794feb07550cc76ef597d09ef84a98f7b7583e30a04b182d68839fcc632b3b59113eda6970743ef11da214e82bfe284bacf839fb2e1a74e1c32ac86ddc138a91b45a3d7ff505cf804af79fb6798b95077f8b999fa32b7d1e7f99c8aa5a7d2bb7575cd56d7a654b1a266adedbb9d9c67e61a52ac30af871776bca494bc90bdb45bf5dbae8056c80e332c80575f57635c1d3f88257687f05418e218196c0dcaf4e099d3cbb07bc11b4d3f480fdc56f7aa22f21ec3f595c69a495df329377ff003b5d19977e081cdfce162a65dd47357d7402d9136bc144223de77dffd47274f9ecc44f1f108d5e6ea668e051e26d2282d3ef7a553cd1c80a76e6e94cddcb2d4889793f7cd3223632ebda53208c98419641f4fcfb890eb2afed066181196047952b7be612882a4c139770442ed9d9d025fffbbaf47597caa26ac1e1c84279f7c2a63e0cc4d666aa02b567cf43ce90b3db08d357a86bd5e36ff6668a5adc635ae8cd637d7d5f67b9ce69d7834dac1cc0d06be5e39fd05967f013e737c130f7643d2cfc47a4ea0ba91099d758596bff9be4084a21f1c8cc387b388c263f615efc016f67881dfa8ad5cef270ddeb3ba8479497899c6f5102f184956d62a3c30662ac1a7b438a2ccbd8e629250395b4192265cfd7c1837186488d0947fe10f008bd0921cde2fe03b0483f44aef8af8b43966d770d7f2b4a5c2b484bd3e973bff24cf61e183d6b45aeb9432e7bdb2a74cda294e8b9cd5b0b40ca2390997b7ce609ea019a3319f0e43d921e515e7c0a5d98d1f8e1ddbceddd8b4f7a58315e0ab4e6ddca41fc68c3b388e1642ad4ce519ccf34ad9d43847c4fdae757df081076baa1887958183902ab5da296faa7f48e7899168ab7a1b595d312117b771d95807598615e1bb2beefa3b6346edccb7d45d79ce37b45a05ee4b78e4d536d10a3376ce3dc170221711542761b5f5d0bc516dabc3f8753f6f53a785f062e0c0f66f579124dcd1734b630bcc3da74e1d7df56b5febf7c109945e4a1b2fe69eb667726fd45a703752295f9a858f74945e9d3cf1681b7b5a10ad67159602761faecacfdfbef0b7b4b2ebfbc59c9ff9a97cb79cb90e667be15c5739d21ed667e1275e0053dabf2a6487c056a64075bb0b05bc116c973ecf7a2966d2e508976f1c1bf7e8cd2ea5f2f69611c075764621eee0e649857259f01041504ce3f3ecdc7b30c7f4cc06cb98525602e8316c6390b7205dfd036f834150dc8b17aa699b66e2c5d5eb7468cea4f7ed5c5c883fa7654d4ff5db78ccccef9888d58371532fedcea961fe8beb790906d8578669e4c16e613435d643abb3ba6868630b8cb2c6392a65c7b571a841bda54bb3c0d8f79d1d679d435977349a52099bf1b15e9b39765bcc3066a6f4e68b7efef39fd7eceef8af4d31346a5b205ac2622c6f1a5f424bdcd712e416e2f9ab0207613d1f3224d35ccf359f459a9de42b8b74873f7c2570087508927bef09150561c8801e949ecda49662b1287e9fab39bc5a545b5b8303f5d2b8f80e6cc2246d7764a7e7638d08deeeab387757d767d5cbd53bd7f715b2c3040b484b8151c2ca7c78bf4bb761e1b99e1d5a976d9e59fc6bb2b78c0b751d78bafba452008dad9ecc5bfea9d2d6406c79651f94ff81cc937d2226c0a71f7c30eedc5b3a20e652c730ecf6698c8d5921ba0b8199ffc3cebdc973b479f06532d591e0ed7bd313779b0557b749cb68b65732a7a3f1fef4a6d3b2b2b5e57cce544f032b09035f93b9b6eb8cb1024f4f6a45014f1f06589a74a1a23a43b7033cafa8bf0782667e6dce805f9f0d420f820bb64d90f7de9323cdc20ce898ff5d846c5bc707c2c0968aad36654ede7df2eeae08b1aa418f6782dc3cd7134ffcb4a72c33a9665e74f09377e55f94d31694d568f769363485eb5421f7f9279fd0fbed4d2312e33fda31cd3a96cb8265bdd8beac2d65eb34b0bc6b6f9cbaeba966c58673f37fd7e90853124c756357ca0ddd5935f0a5d48d11e1e2ffc24d2970173a2d90ab8509cc451b39edfc90af29d46fab5333e4cfd5cfe2a75d27c5fb0ad9bcfacb7f7740951a64b73fbbc2bc1f94a7b09a2be9fe699e4bf130724cccb8cce07a181218b86b9a5d66a005c00a701888863746f9ead7bedad5e2e69eaecf2a899a50d57429bf84182c0a1b8803e2b0d58b7a006b34bd2b373733a1e724c67160e2f2a56c05e19dab4b3c84df23b7a072ecc4795160f0dbff14e70c444e081ab6df820e04c1bb36c4862766ddc54d0aa91ae7d12a1075b655e48f61a09a7c3137df8e276cbf02fea6d21563112c8c42b05bef00571e1add965399eebffffe4e1b543104f9eb5377dbfb6d11014f588cb2ae8d14bf6e7205bbcf48bdbd905efca2b77aa14fa2532389e0d24b689f9dcf3901d9c6ce7bee5d47126c703738762e4f2f351f433417e7901c0a8f79be84ca506421d17157ca6a9d0908fc8a53786e83bbeab5f0f44cc97209b1084c7958157222e6f8b5e131e928e356758311a80dcd9bba24fa8a20cf5ec8ae2292948748acfb2b206c007b497e69006d5a151a20d50e9d94362ebb661609ebde115b5895745f58fb3fa2924a3abf61447133a0cd5c597b8f89272c60e57f830aaf7bcc34049ac6b7edc69402138fa9371fdb73a291f58b39b3301a8c77c9f9efe0343f1811cec1125629c840444fab6c440e376164281004bdb615ebb6c17c34cfe3511b040feb0dd1f7a57121cda4b47586ccc6d22e65327d2e44f39bbad0cb72d1d3e8188346b7148ae934e49cb6719ab39e4a0f30214a86c72d78dbef6503abbace787a3c8cf05af597c77361e6fe62f0deb5f9417c8996f7da44981a4fbed47417e7bd31ad551ff0e2e55d74518e152267ce9ce96657665f8f09c818cb12342767ed4ce7c05487f5bbecb0d92d04c51d6ddd2f6c7678af84db95a0e9c9addfc4174c474eb2d53e7ff93a000ee14abb17b245b5830245c169013d7875e56d133665e34ba4004fce3eebc96854ae778d7521e316cc478b851fb770d0582b6abb86740155ea5468a611923ee310cc5dfc4294c4e41ebe7e03984653cec58be94133116e0500e78849598ce8b831676f302f9c42cc04e1ede392c012f0363756e1c0500b5badd5e7b9969839825bba12794b67653721b0b201335b0b282b6c8b68d2ad46997a7997e04f6928c128150266b589b1965ed138c651745cd4bebc72fad9d3ed3531a6b12178945bc7570143e809e81b6fc63ccb84eb850b3e3a38cb93ea4cea6a96391200f3f2b85ace754bc6ba7a453d5c77a4432ded08456504bd09ee17ce5b94f646ffad56bd68cae64b7aef559015e127885bef5900bf7aeaa9a36f7ef39b7d5f659634cb13cbbc9cb611397007c628157056e85dfe3073177d0fdf4bd7e7d401ade0e328398aca4a1f4aedea20fd920fd7f5bceea5df0bd9d5b9f3bc474fa50680640bd015f707ef5b680b5797fceb203513d2e9c9f43cb4a9496483f5e919c09f9f52d7807fee95721092b08d2b0ab1b68bb6aa3026bf95f8efbd87d1f25597ffa7b3737ff62e29affa3b93b930173061b80a8930049894fa432c49424c2a6aa5f4bf16a9524bac182f216a190c030c1733c31007c20c3017d767ad6775f7779f739868bfefd9ddfdf473efeedd97dd7b7fd5b8995ef8c335ea544cfdfeead51f6a7479c3d3d6dd44e08a0eec846a34228d24fdcfddf838bb281d68bcfe1176357c8ef8f091191afef7befb3ddd7539a6c43a4db4d2051df812120d95ed7482353f2aa83ea581e10bf21e1995a98e6ceafcdeeffd9e3e43f7dbf3bce883ee249ccdfbb7ffe6dff97c1e2354786447d1b2248c9b0dfad0c918b9f385a99c98c73edf5cd4193887f90ffee1dff7a6081d9877c158fffee55ffee5a37ff595af88ee75e907a7e86865950e84c4b41364ea1f230b37290c260f2d9f1760b7f4ef68facb8d8253f59ffaf4a7bc6955de897593d04d2e1b0ff05507e4a6a17f8e2d0b4d121881bcfe5296b555792d1d4749b1b17fbb1b0b3545c01d10245fb0a479fd473fd4eee48ffc819d6c9a0c2e380a95417d5d0365c0ef74b2567689ee6522e286326a7ec5e0e88fbb68a750de0091c3397dce1cdb774b9e299a9faae0d0337aa452167c8b4da52e3d7846d633793fb553985af0a62f3167d198fe7157a491c19b864803f00dc0bae278b48f1070f6c777e621a9a60c6caaf88d6b3db876ac46c22e1d0f4f1925fff44fff934ef57fd7535155b5eaea1d8f629cf2668461da710d6e840272c7a793e01b02b83432027e6624fbec675fb4eec0d58eac349b07ec3c32d5ea07554d245b3089bb3f1d890ecfa3130ee73225e654086b37361df015eb1c366bfef00fff501b497aa74b3bb68c14d0e23bd66c7c7dca3c23c07ab50db8914b068f53706676ff724a06b7aa0f786df9d9cf7ef6d197bef48f3d0a3ff71c275d723c8d3a3903f5c15a9417453954cd4653daca6e08dc3c32b5452b5e8065e7949d6c3eb6c468cecd05fc5c8d958a168cdcd4b75204b0d42c0c96d99ae1e8472fd47e7806ca21ea6bbf3011e8aa1fcaf8234d68fa4e273b0b8d39973211e5a8ab82f0ba610abaa7032ab3ae12e8ed560db5fcca23d312ce2ffa4f0b5a4fcbe29fcd909498d4a0701212eb1f39867f380307328f6701cc08e29d2535147685d859a2527eaebb11231b9474aab3d10a645ede4490432d4ffa622b23ce337a80cbf9468e3d71b6306f14e7a767794f8b4ec043d28c1cfaf516d9e6d7ea05a772f035f5c9da811d341e92739a04b80463a41bc8dffc4dcaf9d4008d989186cf03fc964e85b05bdae051d653d754e8bb3a5c4c603b9e4fd7f961b5d611ad64170a85a91debc4a79ed5e7b085c7a7d578c7ec3f7ced3f682dc789f4d7e5c7bcbec28f477cfe0b5f901d9a1ea917b70e783380d317effe4f31744352a49b426581e7b58c6c7aeeb9fcee17fab2a9e1c70be820ffe32ba6b77f4fcf9f18fda95b68cf3febad0b30f4cea1ec17bcc9813cdc4720cd86d713cfe98d734d6ba9a38febc40b1d9767957ff65fffcc533d084c032d8d53a783a88053f7f2a3445aaacc097f4b8677e8b841b20b6bdb6503ba9586f8cc976fe1fe4cb781d08831e91a7c4586c8d22d2078861d97d04c4588a1ff8927d3424632a6596fe916c10b9c3efe04af9119f638220c2bbffad431bed3c849dcf1bff9f2377dc29ae98c3b93f851b9b1239deb49c984297c192dcacfb174c174eee0feaea31a006b1ebe66c41637cf6e68bc38380f49b5f7245baa1b9a860fa323af8d3ce793171a1a6c04e6b18ee1a5401a32a382472ee9c21d9807d87ffaa7fff1d1d7bffe754fe198b671f7fec2175ff23b614c117b87af6f912783cc1f18d3f00f7de8577542e2d7f48dc87eb64e3e5063785a23d1afffc6a7dd61f9981067f868909c86ffca57beaa6dfbaf9bcf537ac44283e6aefd9aa6d3bffee9df78f4984684761c6878d31adfe2776e56e8c533507e0a978ff130f5fbe4a73e69df3125fcb67e9df32bffea5fdbae275469d0623f0789d9ece006927ac29ebba1372f765599ba7ee32fbee1d109bbb01f3ff173b79c03fd757de68077f9b87920e37f697dfabd57b4f12359aaa0dc9fa9e9711daf35d11eec4a5d7ca315d74c3383c4749229233f30cf3afe7c231b6da98787f44f1d8175acc93a3a003c098b0c6c0775306b7b8b4b79f16d01382243591cc69cd66b308139a4caab2f34fae903a28e8c3e1c8ec8c0967865710cfae48f8ea3d3fd1a15f1240d65c195e7d38ce0675a91a905e57418bebec49635a3c6c7d480325a7dcccf8eb873771a635bc40346f9874c86bee881dde84e47e5a02f5f5bc2e62cf8b50e5227e36b56bc6b05ddea3462c0c3eaefbef28abf21829e9e06aa31bea90ee9cd12e1af673492020ea1fec0df8cd03911f261950b41ff393c8fe778bef6f9cf7fe1d1efead73e795914db91cf949d9b0987acf10d7e830fd3c2d7b5ee625acd8d855d366878d8ff71dd707e438d99f534a33be73cfdda8f9e4f32ca7f503e7b7a8e4071c3c36ea6d2acff1a18d568b0c0b88975e3051904626ce006440367f381e92c3bc1e84d995d00bafe5817536f9ffa944ebcb8f3a56e5ed0630a74e207eee1856d10c43f74aa797bc3fe124715f34d1ab9de3796c7f5e9baf744437b65438c91f113fab4052f0dbbee5156c1fc1457efc62ef4c56bb23d2241704522dfe072b4319ea0e390e2505e1ea531b92eee6832a29de01d39904e4605ab49c05221f4f0200ddf3887b204b471a97058fc3235e0812a77b05ff8389430ac17eb8ff083920a607a4727607462cdf171bdc1cc3a833b31dbd794737774658dd9edf0a814d924a41b391b07773442465ec37127a321ab91a1ffbb6aa8bcb5fd863a19ef6879ca2a86dcd97980cc473891cba8f12bea1dd84d63e72e4c03798a6f7ee866446805a323e20d558cfe8c5234021aa4477395b28b4ba7e84600f4f8055b59c7d109d9fd04ce5a0a99fe64b562f464d4620b9bdd57e858af7100f9577fedef780d4827e18fd32db463fc049eacf6a8fe918fbcf0e8e5975ff6d4f23dddf128e3737e4c89e1dd351ff2e9d84ccb58023075f63135356e1a382f77b25ec537ac31a90d467d3a2cc7a6e0851ef027f02ad20bba71326be0a40e50d3ba981bbb4661cd00bc63fb910f7b94c627dfffde0fbc4b9b4fde65f47d4b3af188846fb0f4a6800cf899a7d2b619dd463ee504b2fa7db22402ba8b7c1247491a87f0cac18c80b9d9ad862714e35426e716a908a6172c8871a8b7f1d588549bc64656f1716282dda36439020d1e12b9dbf27ed9afe865c9b7e747dfc0e07b8fcf48166f49bfa0172bf942f027b58be50fca68a4e928c528618344033fa4590fd563ed851f38dec401479da18d1d1c170bce8d84cfbdf1ba0cb6bec5dd57d3126ca2e1f80c9dd6896c289846a4bc21cddd9e11855317b24cbc755cca0fb139aaa5d3175291f51cbb943cbba16172ba850692c0b7e4e79b85b29707bbad374ec4f3301cda8dcfa64abe3dcf1a8d87ae047cc2b4103bf89ee45bffed2dbf0fc65af70d6df9d3b9fee09ffce1a3cf7dee458f20d0d8479e826bfaeb980e063c5f9ec2ef9c47443ef753dcf5d6cf3432e8ede41fa8e1b259c39b14bc26d3e77b6c56b1a387edfd44439ee5650aec1b1f3705dd54e9649c78f9f9e77fee1b0776730382ef2735e567fdc7d7b4b899b08efe9046736ce60bcf1c46e6540c6b3ea6b774a63ffb2fffd5afb9b01b3c2d583729de969eadfc990d603bb21c1a273757bc907e71ff4f270d5199b4c2a06ec36b3c1c1d8d48a4fb7949f2345dfd9377992e72776434e359197718eed495030e21a35b64191068aef0b67ee1e986a1291f7718ee82be838bcfd31a195ed44edc3ffaeddfd667c93ee53b1b0dd1d3314f1d24c777445c419ae6ad9468d10355321a509832ee9cc8603d4163e7cef782d621bc6b060fe89e52da6b38551a9b1faa72d3731766cac88e97030214d089864003f0e708cc286f15f3b5acafffb9d64c924fc7e1c1280d92a9d13ff9833f70e3a541a11d3ea51331527352c575a632a4f8fca464d331d978aa7f19fd3ef399cfccc642def6fea83e00fab88ebffd0f3d9bfa933ff913e9fc5371e006c228fa9ebe85a167651a8d9f7a2fa346ebcc0e005376a5bd64e6c05a8da937fe8a8ff286f3cbdf7cd9fe60faf823751436867c0c4a7e823e4b8b3e86507d4827cbc26fb8541d163c7ee4909117ffe247ec659ac9c846fd7cf1b75e7af44549665afbc20b1f5107cba71c98417052c8b38a699f1cf3fba8de00601afd373f99578ac40f399c11a5e333357d523774661d04cace50fb55b2cabcbbb80b36fa1506331b299433ae90c6e680238ed006f8984630a6783488f7de7dcb1b1f1c99214082be216d23870f95567871368ccec5cf0871e7f2e9069188da53379cce94902fc7320a65aaa45148bb729ac139b0d9843dc84dccf42ea3091ffda16379eaa63b1b77da1eb3a2417ceec5171f7df9cb5f56c5f082a88278611b27e3d1e9d5575f338c22a6c6744eeecef88ace4121a7d03fa4e922e7e3780625160e3494975ffecb47af7cf7db7e5d85918c1b12232f9b47ac53f838293aa76e9822312a72ce8e67490abad001f92a17cf0999bad151b8fbfbed5fed2ad2617ffff77fdf5be48c6eecd651feed6f7f47bb82d4790e1264da9b1f7f60ba87bfafed207a4c6790781a30a3043baaaffe957ca180ddbc60cbc60be74199d1a4c1a69229676a6b1bdc18649fff8d2b458b8fb851d02ed854e2f51f463ffcc308d4c3c1d41bbb987c49981bca134fe8bb8ff237b678bc150378a077eb246bdb17346dfcfe82a323eb690e307ce6ef7d46bb99d994affdd0ca19d6cb0c6d692ee0045bf95656cb4d387060cd372ede35be9653d9347afee3c027748a800d0294f9999cc00363dd7f85954607bf731d06bfe8161d29df32b281c11ae879bd5b16c7cb58f51c1cfe536d8dd338dfd622fc71f52af3e22e88520a1ea97c8ba653b1d07edb0b6dde1ea6c3ae43a77a44c07481c6c7f40b63b081f5203f20f7823a71a69dea64b291dd303ed0f9c413dfca08291b7896053d77c34f7f5a8b74dd70508406051feef8ae345718532f7d4e417a78ada4343ea403d37019f158b3744d835d5e0f6aaac7eb2d9efa68c3201b2f1a7d345dfccfffe53febeb53dff2a606a7ed19ddf864faef6843845d416e48f52bd33b3a24df57fc2b750ee0a8d55dd29f680dc76611fa104ae7cc714157a6eb1f13ff6fbdcc06449e519a5fa796f297b65c6cbbfb140d4541e27cb3cb2b5100d290e924fc28219d9c43004c473ff2c247bd8efac637bea13a7bd53745365e90fd41ddecf02fa37ca7f84c603c9a2384a669c1b9f1e5918dd672aa5f3e2c449be5b3e4d0bea9293cebdd679ed1ae359d0a9d26343d4dcbd0f0d5cd865c1148b7e08491be3205f71a8a6787487e55903a6e24ccf53d0d50e500f136be8675e32f63a34ff440eed6affcec99e14e45d328389540434407fee8648c1cfcbdfb2e3f00c753ef3416f40e8e76af98fee92ec59d9d1d2f7ea28711a827b0d95982973bb0e8d09f0ec65a8bf388d052ee06377efa80a78c7c80f549cfe72b979b0afa9c9d431a79b3e2831fd217a37403e245cb36067c40e7c91418ad7dbbf24606534f6e0a34026c816747bbdcc4b6dfe814fffdbfff0f3fafa29180cf4e9a22af13cff51afea673b05dcd4f19bdaaedfcea8f3ca6c0f1697624e1d536e3ba72cda22b6d891b489eafa113a33332536604582f7aece66607cfe070139da35dba09f1409a1b023700a67d9cb37cfeb90ff926ca33310e01bcf1c65f7b4383352edf7b61c3e4c73ffeb1db48f99af75c469d258fb6c47a8d9f0b6616c3cd84df2ff8e847b583a99b236bbbb323995097c24e7fb46c8d6400eeeb4855ac71091b170e6d9d5d5e78945123af5f509e39360d92d18ab93d2319d38727d4981aa82c7bbf002a4ec05356ea21f02ed8691c2c94dd14e53d9cccce1953b52755c975288d85a9d977b4bdfbca2baff8213623c34f34ef7eebcdfce6d892251dd0f7b1c73a45c086d8ca6b303450f875670b1b590bf06880f7da68fc04a6ab8c823450d60f39916fe778c3819f58e246f17f7ea105378aaa88692b7ac41f8c7cba79a811f27c31236deedae8ce0379ecf9a6d73a79b00c1fdfb1c58e4d021a319d8bbb349d93f013ad3d7eacb38c6c0865bd96f51d3bae9e7aca766e220446633649904703640386b07ce55d57ab29a82bd1bee0c40675d4b3892e533153756e32e58f5f3352e95755d5b8f980119b133cb466d3854d193e05c83a96d1051bb8d9e1639eb9b15bcaa91ba6d3746af462e6c0f49132df08ad3172259f36eb7c7c2c74ebfbb9cffda6eb0f3ff1d6033383a79f7ec67ecb8c255490b65d9e6d3f75b6716e3a599d65b9ef7329a393f922a155c48a80e44d4fd104e6ce4ce560308d8e4ec6e257db71535910b653d1986b48e225032ca1528e13b8cb3185e0ae4501f37ca6446c49b36bf7b4ee40c1d7c96e758ebff88bbff0af40320d0417fdf45f7fec60714d23441e70b44aca095d72048a7391f06357b181ed781e2bb016e1ce9fa00f0969fdf0d73fcae97ed66c19a1f2022a7766604c293d728a3f9d021cb69b1fd3af6cd22069989c20c9d4e835a799d6f2a07b37561a1f1b22b7218d126b70511af78fdff8891a627e80904e46008fcd0a4e7870f3600d062fe47353e1813530eefa84b405cac9a4ce98aa5237d435a3019b099cb1e4743cfd9b76835d4fa9f1d2a1590bf1cb2b9fd21bde6c26f1e901ea13f9cc0878d19487d069d47994815c6e44e8cb71379ebbbda64d9fd915c9145d1fcfe1bc2a0fade18796b48315946916def06277d89b28dc4cf91b5f7a73ec8658dc261f1fc4b7d73e71d3c92af82428ec1a975185c4f8740e70517d2b9fe69906a3bb8ca66e38f8ddb77230352301d88bc2e2a824c2c5ae310c5c10b8fb5dbe2aac673454b6bfb7a1f7c3385ea5f69e2032365f18f50874301ceb16110c7114df0a1d2546156350c41f3788ffa3e909a325cfba980eabc43710eeba1c80fddf3fc83340bcc1230b46204633d60c4f68c70cb9bd63d2b0992ec18606fcbcd64974defe2a26233e811d4a3f3bd20da2eb1cfc9fbf6988cb97e82ae943eb860a13c1d816673acc9497f596066007f8b063ca9a8651d98f11e4133a3da3112343d765956b42aac481c61a8f3da11187b5135fff6294c53faa01eb4a07fabc3625bef4a57face7541c877ad623159d8a9b048e68fb22ee1a13990d24c9d381d84164adfbd64fdf721de057e4b12c61aa9a40edeeb689369b1b197259e3563eb24f99e1f3b7bb427bb3857f32fadb3006e70c273d0a5a5d706618a39c4ae663921eba85c050cff4e7bd47d9ad0acb9a2e67180027fec44a45f861e3319dcb7391de598d278e4c6f680cec32d1c8dc9954886cee9edcad18056c6b4542ac505b002f159cc0e18c72f0d70f36686b9a9d464691c7a6e3d28999fa71774616532ba17baa4787615b1c5ad60d04a620ac7f5e7cf1735a9f710ef1c3baabeb41b9a63834ecaffff99f3ffa8fda4e7f47d34c1b6f2a3555f9e1f1e1819e09db4fd88c6d3e32a4f8bc99007bcca3bd1e0ceb6e4fe761fd541e74008e693122333db401628d9df96ebc3e62fa29dd28241f19f6a1eb85ce909186693a6b5ed6b7d88bfcde4446909f19f223216c60106c872ee8e7338f82319ae84af1c849da005de0cb74f4a31fe18ce9c734b378d30f973974f009edc07efad33aaba972747cbf40bd83c59fa5d2d8c80f6ddb8581ba185f658d0b3f63d7f27d8427d283e9ddda6f8c8fc3a55c1513832a8c439896f0b09603a3efa88371a761edd077c0c0defe889198cd2b3084c663bf1b29cf3df82e1f73f1777828adff59a8e7278c681c6e64e2c35d9235111d8d06ce2b28345edf1624225246ae1541b6518233f6602777794626647974349e46024d4f19cd807123e11d263a0cd32c5e3804c69acdbe920eec467ef9cbbf6f1c3a1a0fede9a04ccfd8ee6644612d4760c43c2b151e344ae206caf133c78e7843800e0f8c07d33c14b7414266947f5d5fe5656dc6baa56d015aa6602fbca04fa9bdfc2db3f51454f5c4fb68affdf055dd3c7e73753210189dd19751169e7c5f239fe17e551d55321552074a48576e3ebc9d4027ff35d98fbd99224f0d485f42ccdab6d5ced30774a22f7ef18bfeb2338f45785ec829a05fd1b63d4b147e56f90cb5b371e4dc1db12a8bf25b0ed22b445c6f7c1fccde78660bbf8c4e81a67ce002be71272efd8223f442eb064c135503cfb3b27cde8bcd0fbe248473b3930665cd210ea7ddb17699ccc03aa3d36069d4fcb6f19b6fd318d3f0589779f3439d8013f3a03395611dc781523abbcf3d9a027e4a58fe6da305ea3289b706a03212cc91231a0c154d21e7fde8143d918f0e662be16cbff379031e6abff79e7ebb4cf894b17661f4e8740e18fee437cd3ea42f733195e40736902defdb5ff80c1cd60a14d0a17d4c4b6b8aecc4e953e29a86f9e3a9dabd64fdf8b57fff35efa2c2278d3abfb0c9e8c48d888d87d62323db0b2fe8a3aa426654f2da44a27ea291986753dc5808c5471f4e717c552f587e5347a9b82930b56ddd22cfd34068f40f3837a8d735d2fdba78514ebb82df19ca1fd8436d145a465dd672e074baca14f3caefe471c31b01928d6fc4c46927b94c4033974f7c935fbadfca5c6bb20aaed0f78b2b9418da3b860c42dd85531da4a1371670282399d62054561a4cd61c1861539308992df3c5b2f0012c6da43a2e0d9446c1c3547608238fad6d7600f5babd627702b988c6c2363baff34347473167464afdafedcb27166449146af6cb9442ffd4c97a028406c5c848e56017a709fc5bd4ea1c1c194ac8fa801d3a773cd1a732b75cdef65efe142f1a0fd35ad647dfd516b5f74e473ea342a675fcba685e7e64eb99b5893f55a0d11d9b1915d1e9f9e75ef72e220db01b13ba2378cacb94903ac0019955e846a11b179b16dcbcf8f6093725463b1e60bff8e28b968d5de89bbff88ee78c8c4eee54d231a39770843b5e4c5a190e41335233a5c796f2bbb62717cc65f9e7c83b293d08d0f6885d75bbf223cfdf959719e8e28f04112fc0dd36be781e72412f1cde049d5d0c7105b6a0b1b17ec9a50cef43c1a10ec89a3fefdb69f8c6f1fcb198679e9e456d9482cefa388643aa26ef98354fe5c216fdd97acd77eb69546f68c7cc5882f3ab1d4c85a844b6d5192d098c34dcf97820cb668425989f0ac5f8f44b6d1c9f797df7ae74d71ccd9db8fcbdce81b9f830aaf0909c3f5eb9608385518a69229d8e5d471af5133a108cec86ca228f0e6cb97fe8837ae8aaf5c5d34fff2febdd4fb97d4c47803ea63508c7a5e8d03cc361138829303792b30ee18badac7d388d6f9b2598b5129dfed557f52b969a3ae2bfdac90d8b1313fff25ffe0b77988f68ea886cb6b5f11f9dc8f53404c8e4c12f47b3befdadefe847f5b46b2c1815048aed941fa86b6c07c6b346a6af4cbbb999d46ec7baa45e84f840386d3c514ef8e95370ae7960d68f86e41635722998603b490771ec36603076846cf04da3f4cdc6c77dc2212dfc1a6fb63b75e2e01ad4b673ad7c1a2f0f74991ef13c83ef6e301af0611bee64dc65378f4c8b68b45a328b539c8d9d092be146cd3314eebe3672a47246d22f70eae97df942cbbb6dfec109dded691c2a0cf7f8f906177c57da38cf79f1e71feb39a67fac45986ee541b04620d9c8c89ad14d14e3080ed1f292266710bdc3a84f1db080a47e91c188839d34c25fe820ed9bda29e3ad6e1e07fcd11ffd9117f6ac93d828e1b57c8e42e14bfc96ca1537d1121891dc6ee028b6fcb81e0d99918907efe0a92b7a26c107673385e5f44dfccabaec131fd3b1349da86073890ecf549f802fb79ca9610961f3898d1bde1c785b1d47db435923db01599351cfec247293e3edf2cfe81375ddb4b29f2d212e9be44d74d663f1b115bd292b2cfed8ba9d74306cb9f1f1ffd8ed322e134c870309e3d04d1bf0793d75003ed3452a288eb3c083e254ac8c8fe2db64981856dc51cdf5e6d9987018c1f80e23bb8cefbe99e343ec6ef17e57471a98a491d0f8c5658c9c48a570e60f2766914fe361c1cb0157bf02af125e7fe9f12aeea0d3467c17e67508d63a34203a48aa43d7246e2a4bac5c81c484f83a6f36b305ce68f68e4e963ca987d67532ef606587910f92f29a46ee6e8caa1cc7e2392137853c5b5203d13f4e7c33d57aedb57ce8e7fb7ac6c3b1287ee2f5777fef77d5c93eee1b0afecd5f74a1c3d6e7d130d7dcb4e23efcc85b0b4c2d797192919dad733e61fde28b9ff521e569438b17a3153f7d45c0ae762cf2a73cd20446384eb9b3a5cea15adeae7ef2e9279de7fd33def1c206746063e5b96773e4890f9c12dade4ede2e980b62d0b13e6e9cb6103b4ffc5f96aece96d98625e6c04ff96e6582d32c4e9aa64f5ce435df783a19957fb7f024205d0790be37c044c19d62100ca9018ae968aa2a29a2c5be461376cad8c2f74369dd59dfd5cb7270008bbfdca159100f43c39b6e2c3c7514460e3a1a9db88d818eca193446842cec67f3430cc1e7ce4ec7a60cbd6341f926beda6de7a908f87bdac964eac7035d9e35d16708e07077a661d1917ff643d68991c0a8c12f8c700c8a91ae95c1d499531b5ffdea57f585a6ffe987e8be3168c7b4777a46ad066c8496d018db09e846391ff461538687d57c9a8e9feb653df53bbff3259fa4607ad7b713e8500dcb66f1af1c444d151b0d9ceade0246d68f7fe2638f5e7ae90b8f3ef9237d6c56dbeabc65ce07793804ed35a2a6a17e1636a3766bbbb2892bbffc9b4fdb08e68dfca1a91fcae3c4699ab285373618a6cbf56b5bc0859c16791a3f7e77f95c4efe809abfb3f151a34a0ce2ff4f301da4ab73c430f8fb6130d345ed64d18978a0ca33ad748c12c830a671636074603a506d56c200dc406361ca48e7e5854916f5f0e4d914533a1a1b77da1a4f85d3c878798f1104fc86550902dc9786876d94baac65d86d23e6dd36027ab253c7484647e26d63f760e1b353c76971ce1fbea74658fe58cefa14383b813c4c656dc74d80ad767600d7a302e1b64341cf1fb6f2d0155bd87cc88164bda7f5da6bde5667844487977eeb25adb53ee35318b20e754d6b7b9c4b032159dea4b7ef7779714c2b04a6943cf7faa7fff49f09e95d4f69f9e9e1ae15cb83b83edc5e87dbe68dec5327f267b933970bf8c5a368a50ff8c9131cd3109ff893be17177fab5ccc215e3a5656635008ab9391a982205d99df0783c6018f8d0306602518b508272ff870c7a461fb472894a7e1e47855d612980b3b6fdbdbf2f0a988f09b0264205e9d95b93e53c027d8aad794cc5ed385a928d323760019e9cc5cccc0f7f3140d3facdb465d152755bdafb62fb86d9bd3e9ea148c68f92647e8d9aee6bb219cca8726eb068d9aea0c3c5640a7ace3520ddc70d08f0d063631d848e186843aec46e65b899a92ea0645874aa7c277bca6cfdbcb7cfaee87deade3cbba742ed67ed0f677dcd8b46064a1d3fa9c679d1a17fb5afb0b3aedbfa66f7094c172a6c97e3155bccb4bd6cbd6db06593f9607f9e217d618be577cca4e9afb681fa22b5f9cebef302e4012e14bba3ac129f2964cdafd11aebe018fbfd5c98a0eb0a14a9f30caeec02f150507688c37f12d0f8d663ad8e9c3aa6a447c229b46e0dfd0b22d31e894b3d3d10e35fde521ab2b7cc5ac1dd841eb68952acf43cf1f6b0bfa4d352cd3cd948a51825186e91c0d929db0ada71963ad052ef9d8a63f1a38272a807393f8a91a396b2dbe1da12d82608994691e6b8f27b50665dd047f8f569abef169364657f42030727f4067f978bf8c07d26f6a43a80f9e79ede5356d75737a829b061b2dec10d2a978f8cb3638eb389fa5942f3932e51dcd69e8f0eeda9387e174484eb357b615382ed8555f34a6b8e9fae3c47379794c9b40071c76e217c5f8d33e82be659e384e638712c82faf3b3837001ab800a3c78a0d4a9d1a41e5465bb43b077deca330f0b22bfaa94f7d73c2c05b9d2c1a6d67805822101f4a53765fa8f3ed105b2bac8981f9785577ab047f5b2f4872574f1827d8b0eda7ad0377c82db5701a0c8b79460fbd1bec7f540dcfe2983272b01657cdaac55355463e16e9de7e57a7610a063ff85bddd9aca043b13eaa2c46639e4571529dc5fc8b9f7bd1cf907c2a02290852606383738d3cfce60896ac319cd3e24cff38bcdcf51605f9fa934ef06b24a3e3d29921a1637de73bdf76c7a163d2b95e7ffdafb581a2b59d4e9160231d8b902dfc6cff1bc00583f49fa9b97f7b4bdbe6e7d473e12911fba3e70927ddb2d66ff3f54b6370233272c937dce244ce348da2383ef100b8dd9c157f839d0cdca87982d365ec3a8d2c171a21f9da7296221bf8701992c2922d4ee3417274eabe3f09573655eaa4f87f489f02a3246aa2ac4c1f0701a731fb95047534022319c7ab68c84c9b5063fb739b7ff24fdae4bec08f3510a7d9e1ef6f4ea893d07959e7f0e7e999d63854046b1dd6649cbaa6d3b8418b137c69dbd679f4e0a6f0ecb3ea30da286111cf81575ec1e0a43aef5ec187359e3ba9b9a7a37a4aaacec8415f3a99475f99c3491746b23e1f72454be23a2c2c7e5e4309864f3803c82f45f247e7e06b5fbe29d99779b0cf4d063e6d1a38103b634f62bd16ee696a7f6986519f603af0a7fe1bbb70ca818147b8961b785c520eeea6398aef4d9e3ced8f915738f17d7a8e4ae699ee253cbc70d1b5f4e557194b998351ed5c659328ed15fe501e3e6ee1252ce32a7312de07a3fc0a2f8f1bdaf83a201b2217b893e9c33a340c95f08ce6677a2e9403a4401a681c690491a5b48a706665c1b238de31d4f317ceafbdf3162f84eabf7a0c53398ef0300a70f4cad5203a46113a259d8129181d8d291a8d8f3246460eeaf21903be70c57a8987b97ef0ab972ce954dea0b156e831d58ccefa47c3674aca5aebd557f396317ab31e62bac7ab398c84f8014a8fc65ac3fd9a46bfefe91b1fe8637c757ad65e20f9599a78706340769fadd56b1cb3f25946acc4390ae0323af221994fead02c27e37d4398860b4e75274d80b6b06bdc7262ca5a5e79c009278f339dd25c4f9af2212ebc741b461972e18fd708b9a1602d7fc125a1bf09e553be30b8035b04f1db559fe2972771f96dfd22bf65ee644642e309256a9eb8b093d1093f71ef4b97bb9d200fd1387c86513b68ac15d815632b9f4e468388abc229cf97aa832af4c611e15cfdd814a0a3f123813fa79311248f8d15a6676c103ca7c3c1bed3e9ca79463a52b7d2797ec3793f1a3e7f6cc1333a72a0988ec7ba8f07b8c8c3177996475dd6c2113938de61a4d3e878d2775e79c5272c40a513785dc5e6874626466191b8e1b3aee4b57946b5b77fc61a2f36b8330b07d9f1891a0965d64323b63a641b053e641accb7edf906e10bb2833518df3be130320f8273bac31c165dfd08db866b9d033ff15a5ed9a5233e61679ab22b8fc2aefc62af4bc58f29064ee03f1d2bfaa794eb6d90ab1c7af695cca298c2a50705aec65dbf26866670af36b49cb838859107dfc7aa00c27b0927afc22bd1096b9a985086571a1752beb823855c0cf1e70898beb1f5ac8ec05488edde3aa7f477e318708b2783d430e964748afd592f4dcff4bc88e91aeb97777f95d18111489d4c9d8617fafef88fffd8a3088d9167678c628c6e3c66884d7428d9aa4ae63b8a84da5e658da7228f246af07c5b846930cfc3e22fba0bafd7b38daf75e24f34baea0818a32b0d1e1cd674cfea277659c771548ccf056cfe48457674f14b84caf18c10bbb1851b02370abeb8cb2e22a33037113a1c5fd20287d1123e9d1ea3776d899e3b1fffc65e11d917c695aea9f90306c23da1bce3c7e017ad65cd37bec22d6b462dd24ec615e4f467a863fc0b0c1ec8e45fca8349293e3d651863f0617596958fe974818656e54b00002dc1494441543f7eb90bb760a3d5a7c499a388e0048275cd976163731a3cd285376e391a6dd1c29b02f8b326ea8f50d07879a9914ed6105edb28e0380c1e5b4e39868a3b3823156ba04ca34066d3e21dbf9ace83691a17a327816f7ffc5dbd8dcbb31d74829ed8f6e3c8d9ece8884565309d1442a65bcaa30b3c3997c86302d67e1c85e24c1edf17fcee775fd16f85e9cb4c3cbb437feec6529b0ec46911e2e79f8f1dc8e5d3644c4979b340fb1a597b89d241e58c7a9cdc6033852f637134891f33e75d2a46abe798ceaa3cc7adb25e832fb209341214b08dd44f000057087e7189cf3123680fd1175e66e545fe4c377f957fa5b76ce97faa8909b12657724d5137d8b7f1b175db82dcda53dee09b7e1359c2a96f71cd1e26378136730358b63ee12e208d617635eecc9f69585df8993b4a10aa8c33f015dc2510a1893636e800dc8199327a5da3221ed2bead861a3e77f5811f7c2a677282ecbb121d97bb36d33ff8b26d4e77622b9975191b0dfbcc60d4a163b1d942e80e22d3b986a82c2e83133c8d541a1d594b72ca9fd750f8c547b6d1f9ee212f83b2e6e2847f6e1ceac0acdfecb9543a23186bb26cfd338ae506c248e3b784d5c9d80dc457cf7c4067fd34c2f2e55ea67b1c877ae123fc56d9873df5c366be1ecc7943d7e5f8045ff9cfc64c43c22005e0ad57e2c20a37602e298dc749e39dc28a575e273de96b70f900cfd2d25374ea73a56ffea40596bcb4b25de151f9e557ac1ae069f72676f3ac75e3928abb8937bf82afda14ee2d7c1a337a8d8ae54c567ff71b7e3ffc8e60b30cdf8a847d2a07b979439ad1045a3a199f0c38479a2b4f57d0e8987438576df0736650273934357afb6d4dd50483270d9e87ce34eeeeaa859a4f54934ac74e8a6c3a6f3712788ec768eb1f43d748c5b3b51fe8a1efabea507fade34a4c4fdf7c8bd76a1891f3ca8a6f20ea5c7dde959680c3e9a4fa154e75b26eb8f0c3ea34103a198f0638018f0daca33ec98f5f28e660301f427d4653ca1c87ca4dead4756e69634fea0a9ba64a57e7a86f1b837386c2f133b4ce4fda784abb2eeb27e57738d3077470ee2b6d1b041bbe96e90cfeda37044084b32d0c2444a2640d26b556b8caa36cd3c3db226ee8a2ea965b7f3486f9e61151d09c72816a728e4271608d2ca1e3552561085185348e32bb1c9c06e308017b6de8200367fdc19d97f5041d8e1f07fcc53c48a5bd11aa13f8d56b4a4413d705873453383a99de15d33a843515eb210cf40e23273f3495634ac7baad7cdc01d411505255e3518f674e1ce0650dc7e90c9e473152f1d0978f5cf2e62f273cd8f1f3ec4f17a6a4d5d1bba678dba35724d5063a3cdabe277b7fa26d7ca68cc8a373d19c19593fa1ddcc7ffecffeb99f7fe5ed820fd857745a42a7acc863f4c5c6566ee5908f57129b5097ea08cd229ac2d2928d5f773c288ea8cfdb00c456d98fd8e183aa06df622ff9b70cb65ec3c954d4fb99571a1d09f03979610e3adc4a03a69042d3d6aef2a1ce09616b2617be8181535ad2183aaa2cfce629ad6ede5d2ca0711dddb870e2fb028cc7061757f91b207a620bc8fa03876d7c1a94a76bcaf34d07d6353cffc931580820ac91e4af015e611e1d58d3648791d7cfdf18120ce6d9925f7b51277363d6d4d24138347cca79484c47e4c02f0f7c33f5a353bdeee75a74d0f5f11a19c4c9954c77a5876ca963eb03a69dfcae32bea08c245f5e6207f1c37a378bef4ff0cd454f576d26f6e4fd384664a68a8b179d6902a91e07c2c41e611b73ed63e41118c9cbc30a18aa0b821a06175871cdd768b9c1410b09a8f82fd4bdf9011ffd26024bdcc401403a0be2e0bff898e77002785f18be8bff81630acae109dc31f22213b94953a8d4f0da3a8097006c8aad9f5a850aa253446cdf84027f8c6f8647e1e113deab93d14ea928881aaa50f337716d3880272d365a84a46d8e4276068747616f7e6834a3a1f25a0adbf8e7e60704558978f1bc618af35004a05ed9a7933d9fdf097bfc87fd5a94d67cdac1644dc6e84487fac563e7615a6d52682df583ef7fdfa315dbfd74484e8940872f10c108d24e6579962ba982af1d2fe1f939956888d19be929e7247990cddbcbbc6cc97b61fcaa0cdbe9d9f113330c8140c132f9d54a781ba2cb94b9fc848d7ea863dcf201aebf455f9a2977193cf9034620ab6850863ef647137806cfe8203794281cc4a40589db2897a8b127f2843c79f43a7572f9b08aceb77c5168482f05955bf30ec52f989659bd55b6e5d006a3c1d67b1353145a6031387602d70c65a1c2436123277fbdae8e348c9dc7c0412c7de0a92ce4061eac7431caa2bc47324601edbefdfce77a81532f72a2dcfeb0cea145ad342877118c8c51a97c762c1929f65785754712126b2a3e76f99d57beada9de1b3e70fbbff52b8a1ca6e57405af83305231aaa15d4687e8b84fa19057e9d8843ae033656344c5163a390fab3905c2bb556ca5f3f5297601f90605d359b6d27d83d168ca68e5aa396cab3d9859dfda64e118d7995d569c2b1f94a5acf562e54b3bf2285bf493ae941cd23e258e4c1140ee8d03d300e03ff2caadb1042a09dcb2a4536333b1452004bfba3ab6a3433b6a1b8d32fe85822974352eaef2a376d88219c08859e550987eec899cea52b5869642df4d456110f52ecb821ed2430e383a560593adc060ad2865308c90c6611c67816c198363e2e24f19f876036c9009922e8c0afd054e0abc8daf29631b6d940fad696c4052667493c716469bc7bd85cd8e1b3b983ca4c5234cf3f86594af7d2daf8c302d64678f45150d1e56d807bd0068681817cc41aa2b571dcaeb2f772c9e51e953d2fcd8831efcd291f834991ffaeac815a314eb3f3629e87cfc84947d2a866ca8c05457183bdd067afa99e206704feb0b2f0c07405b19f13b4453572d2ba1e50eb5a24c41274f9949f1bf02f9fc4f7a3449e701e108c3b774b60bfea00c5feb3924c67311ba03acbe930622b8db90cacc4f7c72546fa8950fa9af1813bc40c727f01b3ee05b18f4e228fc51cdb276d92ddc0ca01831a121532ba6c0387ae432ee5bc59b418c3c2b7b551eecaacd10444d048f736c18cae96f8461881521d21fb874329efb3002e038a68afe0ea3361104b4d13526a270468cb8d50129d182726fe3f3352a7500761311c8e6023b79ece891cef45825face8637123c748907fcf507ff9c22cfb40ff9f066a3e6034ff3830739c7c83736723a449d8add3f4e87a853211bfbb0d583231a2ab135a5887fd19c82fb6c5b76a2d7e0924ace20f3b4dec98a215212c2bf0deaa0133f971d75045578eb3a3e3097536900215cd858605ac15de7c8d79fed3783c0696f95315dc279e8135a3a22906059433ffce3a7299032c121bff1629d21869b636d520c0dbad6e720998f8d1b9218ba70e1017eebe44c9782f8e4cb4315979dc022b9602e657ae255b996c1cb2eba2871f2711ae348082f671879289d4d08b6b579f6e483bcfb25e01088ae32431eddcdf3700c15cb33231ee8f231183a1677a93ac436688ae68e851a439b6a9ece953d7d777ea676eeb41c8f62faa7d7f5f951024e8770269193193e6e25b939c132d3178d546e488c58134efd698484e5bfc1b9134d1d9dd6dec1194071e05c7b37eec83bf801312e482e1607df7daa5739aa7cd151cf786df81d3c30c9bba763dbb90b3a6e369d8b87f5f6fff05384a8e4504c015c1185ad3301b9b01d77181ad6cb094b7acc108224afe253da6e2be542dc1bc6595f671a1cf285452b6fe15384514b5a00eb0afc54208c282eb35666f3b7d86144599d4925368d583f98f62e9a3627f4ca4b7718a12c6f1c721b90724aba4d338ae44333bc090d9fe80b9677eca443f4cd2855dd4164fbfd397728fdcc29d33ffd18037ffed1038d8e74b8a7742a8373895e4fc1547fe5e11867236bb45ceeddca3ce8f3f211a979da076414285b7515a35290c2183a70e3026fdec978de2aa3cb3dc19a1f45453b6bc0691b654ee13272e85826273f30c7abbee29560f57a2832026b671beb6e0b701f6d86ffd99ee2bf2937dbc82319f4e663e48d4f8d7fa94bd3a5bda0c3d66390154566e2a631175c6f7c1408e03694f1052abcd250d2f47dc2cf72611e8c700b0a73ea23eb1a3e80f31e3b8c1acd78987b1be46a3ae75a74d2d880c590ead083a0fc9cedf31fe4cbb9cf6963037dc54de6e57894f89830eb375ea8643382cf9df1c9343a14a7ed3959c15bcdeb1ca33a1f232f66f46e9ddff71ee6a7c2f85232b65e532878bd0c1581bcf186c6c0cba534055ba28d2a2471f9dce00f9e61b6fb283d789cfac00d5e77dbc4adbce4a897812b019fd5d194aeaee13552c01f7b17292c863e4e0a2e55cee08aa788dae902d9570a634e398e2c50ac45cb0dd025dceeb3f16ccb6779ebd31cc777f7c12a8178ed2e3ec4b4ce3b1d5ea627cd29d4026c9f2ec3a066bbcc192a306ef3ef49ab01f3a6f45b8ff2d96e7eb8fd6e985d3f7bbc1cd390ab1f3cb19d351edfdb603ac7363a9d222366a6926c46d0a998f2f9277ad4b9f8fde0e7f4109b0f9f428f3edc00ca1319deac38ea0e2d92e51a9dea9fea5f3f19031e1488a969c993468842ed28b7c84e635f65a201bf7c4d785ee0393827f88a6f8987dc69a14bb7d0c2cba061153d6335e9b9e10e1fe0b5a5fcc0b2cdc30138e885e311bc511ce084941f5027ebb79443042ffbfc608ace09200cae01f7fbc6f425515cff5d7d06ca897ba6970b2e7cd2c9545aa64739ec56b68e3bf14e010bb1097b480e81c5b0c9dd887c8006e3008f667aa83b1b1dbcd0d8d75ebceb2792387248cd100134346047c51a3702d946f76b2a8af9dcf5873fcc0fc865f78fd18a737fcf3faf13f77ae8fb943e5bc614135d08b58d7857988b5c63a96ae1f1cff2a78cc672d1a7bc8a418cf6845589d44140b92214e38cb94b4e5e67fa2485c6a4b7c0c5bf74b96521a2d60c816447626c5f752fe81dbd4d1e287c6f545e4ac05f1ca7bcd6448f29abaed8bda428a96ce517c5f69141acf1a7c4fcb71f116f198816f252c764d56268ef89eaa7c6a09ce92bc9a9ca5976e7e3a6adf4fbe293106135fe4c2f1c6c10ceea58ca7ad88fd7f09c51e1c133a81c16cea8c12b2f6ff92de9d95a87661c1f23715860312c0edc0e489e337e2fbdf492dfa7e2b47ad65473429dd758e621385590d3f6a84cd34ba87dcd9ff1b6397650165db65f8abf704118bbb7aec1bae6afb476e70636b5ea00c092637f212a1d1e992e8b829bb629c1472de31d6a0e463a9b3363eed6b7f65743e5cb8038ff5d7b96010c7f4f7d06b698a69aa903351c305bef4e5e2eb649b0e2c5fed86d199615a2d83798ca801bfcea1f3c600f85b3acbebdc6276d64b2859fd42a2ba35381e2b40ce40ddbe9cd84541a9b699031ca572930cc43658c1e3e62a5f50e26b326e3e76fd8cee76c23b4fc0e34f8950b3d32c425a98b7390cbb4ef377ff3f38ff8e54446447687cac3ae1c1a47f08693e2d34ed2d5730439bad5c3d642ba68179d594e2307e112e0036ef1c3f7b6a25d0e1ffdd962f3696e33ac4ee5459cd0268d2de878578f8d9b1b54e990089bc55b05a5367b659a2755895ef38efcece2820523fd473e8803aabe754fb51d65e31f214777c860029f252d79242002be47a8eefeb0108497b06d877eea5b38271c1efc15d63886c0b0ba6cc382a3fd8653298055887419957963b38450a1b0d2196867d6e1c4a95ceb811384948788b84bffd8fad654913f3f3cf6e687ce08aa93bdf794a8fd6c29cf5cb28d6a0ed22fd272ddce03de69df934fd639db69accf84b0dc5d1b805d43edaa2f5a5eccc29b4ff9ad1fe1513ea53fe3961197df594ebafc1bb7528b5f1ea5cb888c5c410e3fddfa8c8283a3fd068721306d18580ec9818111eac1858cb2092e1b7b521ff7e3816e1d4d170616415e84b91f0802f9b0707bda597736dbff80ffea23e8d31647ce3df8a71f5d1f217ab05ecafbae5f3180a076eb68304e01c09b0f231a00509129715b16f8ba9ecac71e544d98bc677baa15afc7e8641a697856c6cb94744bd664accd682cddb88897cb691895af63606dd02947d7be235687a0fb7dd4b7acd022a17438e0bc432f7cec5566fb65f3af9f16ee24c03dff4e3cebf78082c56b7cf22d3ffc24f62b24d96be452b871ac7d1afb92bbf15741d0a034b11eef3b462e30bc60f2611c0e672742aa492c7bcb0f2ca5705a82d281426578640ce7d1959cdb8818a24b777e17bf819df92b5e6d407a836193dde5ab54897eeea13e05f94c83ebef2e864b9800dc01189599b8f0e4aff837791ab1feacaf78d8f3c424222e309c34b88c66798193531e39b8cbabfbfa092f8f708fdecb540fc2ea759bae7eb01cef6f9053c05b567d1ba3c768689ca6cb028ec0aafe828facad534a8c6b9bb73e4b9650d0e3e4e5f4e0c3c1e52742d8da769227afdbbc250f3678d84c16792ad3ffe4ef612eacf04d67a9176b3b3cf86726b0743a79b8059f36e342f142d6b9913432673d3d58a6eca39972d932547cf865fb59d63023416a541876f7db157fa6ac69e20692cb7665f694ba3467f9a9129d0a3bc3abf572a89c2dfc8580c4b3d4f9a851e2e4725d74a239d3f0a8a9964dc68a6ff8f28c74f368365be68c6854242f70721c8a914cef149bff0dcf51e4944b1a1c9c5ff8a089cfdc30001c3646357b3832062f2e0375db565e0fc5c5b51e95a7b8f0d22ddd2893cec8e26fe942dac6fa32f4c0843b8a511e3ea60a6b034313bca4533857d1579f8d334cd1a024035aba463bd3a26c6d0c571a6509b75ebb43a66ce92e06d681d8bcc40514649acfe605ffc51b06faa3943ae64c2a32ac23b40a1325735ccdbe0a0c7cf1759e0e9bb0e177eb0e450f534bb2755c909dd0d9c55b07ed225294393293ed706cbd4bb795db5cec2e59beca605863eb11e51fd7f941bfc0a9ce066f4eccfbd3dddcadd8fb1896f039653badb29647e12dbf29f0a801d383af3c53bfaa503ce2ea6a9a2928ec2c275d9c150b76e2165edc963546fe89236a9b80ba84e2b91eb695cb07c5736cfba04addc017f872b912f809d68d9383e66ea8ecea575e9b269c2c3b0a9a49933e2c200858a7447286e9e27a1b020638646edd9457fd3b3fc28d0f0371b07dc31d2450aaeb70ae24185b6875dbe566b62edbe6055a75b021b08b265b5e6067bef8f9c68772149690c222c764f4db154739b8a5692c2084369b24b43833ea40a58027145c06ba4174b2d9fc9875d9bb74329d616444e3c33279d5044ea187477921d750841abc71aaa70b84252b4cb777a6543274a7fdc1eff52edf9634ae9c60065a7ef525d0fbd2c51b2ab0925c9e83ab60f69d3c6721f17f69c6848376580011292478a99c5b57959d6a59a52adefc83b3cbd4f2cd74aa720b5aa97a61e489b4922bcfa8a3b4dfb9b37e61506a72d19bc4d6275071b402624ed920b30b9da0d8bb26cd878a6bdba5495cbc7186d8f637ddf8f409b0d6e5157ecdaf131fa7f12771055053879b0d2e4d63d7a64aac32c8a3fb913cd8a9b9cb21c8e21faf97e459d94c0d754a9ee9e2db3ea80ba32d7deae6802cb64e9c46d69629086219dc92ddc9c127b645f6b27330e15dd88d9c4b3938d772f2a75f6e79c975878e29dbfe0fafe826369566f7972c7070280e92e501506273a33c1555dae48d4de10aa74e0b784f22fac506a7c3fec626c8e85cf0b47f008c4f62eff6591e0180104ba64bd906518f6f494dc9c8ab1d64b9b9c6bee02c5b65a6cb75b11edba1ce5b28f4e8a6bffb7c50f83586169856a538f336dcc728aadce29103f70efeb24008561a1991e36bcb95893f52e68f9db28dcf4b8c9a26f80ce3accb9081c20d4d3f04bfe235ff508c1e573bc8e79f0a0fd9f7f1286de2f8a43048cd0b7eb51d79c328acb18f7280d6c6a5b5b33c866422fc519f94dba6ddb82dc30cba576890e5bc6057b9e8006ceb3b366da64e51ee472207bcb6026aba3b7ecd138f00eba04b4c213e02fa6ded5b0024f4604bb3c10116b6601a6ec306c30fb9951e9871aa0799919df2f826e0fa0bb97743f1af3198c0f859c8617ecba0047759de42ec848374d189af2b13fe54aae544688dc121569f8b0c6434e35d2da68ef06177f1e77afd9f2d78810cabf425478087d2c5bdc6e0a3f719c8a1e9b5ac4db2e5d0dca1bdf02a0f9b6c315b1eb0ea0b9ff82f1d2cbcb9de17ac9d68a1217dd15fbc228f98f2db808abb9cb2f2b3eb8dbc75298fcd67db249865a5ecea8b4a3d7538d32d870761d12f058391ecf61b50f319ba60d161e28974a353df5848175dee50c2368a18e9d010a8e3a46f7d5abd1b57d7e61b9bc971295e41335d1ce5886ee514cff1c9f46454878074975c907a8272216f57c411a193a96ce3f3bc4ccfca28e177a47fc6f12aa68c3e57d8ca87651c76ea71ea07cf3314bfb0f731d568e5dd1860d3e577e6cb1b2f54975670b7a8efc737e721a751e42f7e2dd77a8db23ece280c795b3728a25f69930f1c3cf22d2341c76d3e317ad686966c9af217ced4e65ddc69d04374dabdd22374899efcd61d5b239dc89a0a70bbee0adc57ec1a7da05a7c4989ae72290b3fc1565af8e61dc2ab3da1111584ef134a5b1fae3599e96ce4764e91288be1f7397e4bad11c5354ff4c21278638481d70b95cc1f9d8c4d10d665fa72ae4e7cf0111bbf9d2c925b5a981280368d985b1d4f838d7e5c6ef4840b428f70e6cba731683b5dcd22fba44b3a8de584574c75906b6c33f9a46f7589bce85835cb3bbc4a975c71b69cf0c75ff027547672a73d475a6ad4cef0dc7a5da789e5d378d36d9ab3acfe680d1a1f798cd4a3631ef8ce4d00b0477171310a6d5549707dd9729c6a99996dbbd1c16d9284da8e4a9cca05cac92fe62901ba2504f6d0b5b653ee278506986f9817a1318838843c71d32dafb3c02b8cd86969e5723b018c09cb2920e8ff1c12e6d73739f94187e337b75897f196b41d519e6211fbe3b8256b589ffa00aa4e535c623bec4ed9420a5dcbaf3c8386bf78be12bf11a3d7cec383bc8124ac0b3c4fbcc8c0963cab495970c30b26bd11998be8816db940c90752fe81222c3ca3c7690bb2af36363f2c5779711b5be2413fd21c9d320a870eb8adb142b12190a425cc665946093959c1bfc7120707fced816402c3cae136ae576eaaa01ef2f949e385067a3a9ecb053214dba01b5a7008e8f67ee1b47f6de19bd89738a24cea986bfe6442999d37c2bb64a8a2a56dec1b154e16beaadfff68059e2aea557f3eacc31aec1d7fbd2a3f4281d538e094eb7a2ad3894f079c3a51bc6c416e89955ef083c72a370c1c73706e54575a40670edec6082e340bd7e6c284bfb3e648170e5d7c9954af67270137f256a984d46edbe2f2969e99a175517906aff4a5aa4f0a272eec8ad3fce9efd251765ffad424f4e3631c76f8c39e11a88f5c5ccaa568261696f2e01a9ccada00385aa02f338a256df2cb459a880f9c261475e2dbb651a41da73cf690f64846318c471363d7a9c9f8ea86793a1228724f27a25c86e2a859fdc2214e206d13b8a8c7d9af4094807f0f0bc3d79f88d36876ca803efa91ba2f8cd43a7b1c16e311130796e743f98e16919551e6569af8d86d572bd10f5bc0c6d9209106107865030d2c3a9133fae8be4ca0204c9c3a2f27afcde5c4b84de7b59ee815dbeea6a1586587dccab22523acb0c6573d4f3e6e67e6971940ca3a5259681c1005ac436d6a6c39cbe52432164262e739a12404141b3778a98341b844d59f96db11cc03c6e099dd6d852c0eb66b148c4d29227dbb26139c061761a1a0a190473021653b06569a36d62826a3426c1b8b535e361959e0c05d317f7d28cd2608b87cb69b87d23e2aa5ed7d6c84a4b2907f0de0d079e18b872d8214a009d6c332634b3726621fba000f3d24e5b1e56e66c105ab32c14f79e5a0cbe6595ce284e8569cd05656e84e79e855bd930637badfc2b7fccad90768a1d932c22758e1d1b4b09c2c2e99ad8d522aa691152f75548cc6c34dd974b4c937329b5bdce688d180d8725c305c5420cb5d182dc3d034019b3658a3a3e0e607ad7100847aaacd4c6ad3e9c3d3079194ab6d171b0238ad0bf2773ad95908c299afb01376322c7cc18e56dd328c49ba2e51be4999eef7bed499f80514020fa4395ec539359ea38d2fcc23068f65e34abc0d8e65283ef195334f2e850720778e9faf3134d53734c12d5d62f8568fc629e1ba47b5abdc8d436aebb4f915b67c3a24f527f1b5ec96ebf8424070adddb821febb62275fb9d8bf436cc3470924f427e460919f9471a67cb041358650ece7c28fd8e5ca87924e414675609cd86aa001292f2eb16d9a32cba82162542dcdd238660d95b96f5a415c7ec08561fec6fce597d64db1d674b1801d63d091db9edd40a5ae0c290cec201e8ae2c607345652e34819ca93fd9b77cbb419c2ee623e1197df2d5b34c33ea3cd0898aa482e0851db122d0b79f0281fa7872ebc427736c0f8a13ce01ebd9d32b8657448a0f820b1ab76e97aca0607244d95bce1b108c6efeddc91566e8dabdf6dc3a95de1bd7665b7326e4ed0f6c7db499757794722fa543ff846cffaad38d65afced47c71ccea6ed847edf40873bbe50d1b8c43758511fa2272d5e467596cb6061cba020c259709536cfa183217659fe30f2ba0ebca1df4277ea8ebebbc8027e69f9c83c4948af1f66c749779d7d45c7a8686803ee492f0acaea49014b67390b6927dc90fd9c2ccfcaf8dad4da61d4f332ff08f9a0476cf5288f54746de8748d46587cc7e864a74307cdd00b561f44d7f28f11f04547f0535e26e90c918b0f5b0edfd04442f99de5e197f2dda9921f2a093c7d0d14f9d14199aa31f0e25a5f174291101d9b1329b40af6013c8717be5bfc97803a4a6543147ee431dac0303543f2ca962c59e5e19d4cf5a18e08956909e3ec514918e3cb21b6541b8074f92e0ac457e2d5dd43cb377b7080ebba0c47aa42c45b577cb0541ee1514572a68755cfea1f1e1062db6d7dc14b6385c2286ee4e35266076825cfb2330d3ff3f4250a57304a55b16588390a59652893676539f5d143c17c87912963f940125ea462587418a18027e0fc9461bc8008b6bdc0494693ed02231891b21e070a214c83bf6360e1037ef821e8d465d3549e891eb85c71ce3ce9add3556ef2f571d95ff3c0d370c027672f39763a40db728eb2c88e2e221203a8ec0ee3a7719d569bf3c90b0a482998d04e78da188c41941cfe11ccaa72c95bb88ba63d6cceee0fcd8a152c9c35fd2d6d6c882911129ec04d03fde8e0143ae9ef268c9dc0968fc4002f717440f4b704cb6033bf61b532c5b1036451655c49cab9f88d712e65290f152aa14b373f8831fd676fe9c33afa9d313f2fab20d41759b331a13b56dd09ac5ef0af26cb0427d259711ad936a2e2a463d63d9575cb2bba4311c7c368c336fc1606bca11556df00277ddfdfa649eaa4a92dc5f965716ca199e69f7536f0ae9ed563f3ab2f279ee8d405dc3b9c0008f70e7c334e0a3d40f25ffcb001b7c8b4bffa6f952cda05f128b573a841f317a2f441fd31c1a9e60b03afe993c74369fbcbcaa3351f37a532613d2de9c6517f0bced012429f9cf9e912ce292b5fe3a586478dd097874f7ce858150fa4ddc9c4876f30f20381e70983f04392a59909bc6d2020054ce2afeb36d79d2e63aa30366df1454152013cd2c9439b6954cab812829374709047fe967730ee5eab734beaa7c685178f181f25a481817b96477629896bc3b6bdf658cd5c4c50ad1f921f24d5ec56210d587a15e44669043b2275a2c26004069f25035afdb5bd545f3b72985a2fe134803f8ede7c545819f0ea0bb1877996515e5b6313dee9884bd6b2ecfd6b75d984fde8a8ff7a184d5a298556d664ac5bcbaee51b2e0ea20fad29738125cc914223209bd617952372a1041367f7bd325ee2ecf12aed306a5dc62fa9dc1f6cc5a5484e8e6a82eff21bbd4d6125efd00200970e5abd1befb2d0068ff4ad9cf864d343571869c2c9f34ca774d70df9adfb38af488a4b9bb8ba986ab0a2eb548161a52153cdcf7a1ac2a9c2d02f5809a0b53a53bea25bfc682c58c1b43957100ca67b0569c1c337124f5d0d598694e1c683f5fa2256c0539868fb115ad5cfe058fcd261935c4083bdcb6dd46272d3755d94ddc522880e636a90e7cbfc6882ca0b33eb03bfa2e2b34d6b14803281347f7794ada3ca4ff874b2f545614633c1f864f7cf3565e41371377ac07b7834ae3e659d3cfa3745bc3375f8a68fbda7b636e390757232b7615e5e27ccb863f896813e5bf7f283fefc63242fcfc6e51d5e270f6ce22f53e6f204b6e5aa1c5daccff601b8ab6e6e1da502950c6af9380bf8c4257dcb322a88de9d8532feb6a094dfd0b470788b67d76cf64b28369d686fc8291916b27afe153df9d38f2989895b32b25bb2d82de02d1e0a6c25b68e9b01b0bd852ff8a9c0464360a623f4511ccbbf863aba313c8c33c2c1dcc24365c795451062a98ad5acfce52a3e3cfa847ed0811739df7df76d9dc6d78f036aca5839e8d450589d6379f286db4781459ef80a3e6d87ee3efe1716cb5fb5b771e4a7c39846aa56c7f2280ef953f66d3e36deea73b59bfcfeab9cea92bc6acc5bf08a799d5fc6f70f792bbd59034e00977fa7c3a6a5c54d071100fd193238e49cc48818629c00836ffb41b25ec0c8d0da4cb950970ee89222d3e804f97446c1f3df3cec637172b052931e596562be941b5e287901464e8be1007e1f8f5ceb8ef2e82f0ad3ce890f9ce832e0625c637ed9705d1ce8489f74a36bf828e372f082bce6ca1ed2e971c81d6bac8b60d8c7b31c961f7d81939fa0655d960d11985d0312909e501d5d270b6c2de2870b6e1d56baf269dc72f2274ed32d6fbe748d5b8e6dd1692965ff15ef36be347017d25182b5796d9daef2c91b0f12d221f5f5c46d3b388adb32c65f5322d9233e00f357bb512e363a353283096fd7f1498852e53470838c3bb2c0107f734c214256a179ee6ce0835fbc55acc4127ff0004f2413464f882a5749ecaaafec4f35ccf26dbdc2c0769a574a497a245b480723606502714305355fdafbe062b1c2b2a1c0021ad3b12c1122a5a419a8e13fdf61641b5f9d2ce160bea49c0918072722c94758756ebe54d870b583b2136ebfd486122a067ec5a3b8f85be64134c9d05d4718b6ead1a7d3bfe8df1129a4d5b7f1fdbc6ba73d7bd1fd3e7bcb455c9dbccfd317362551ac7a9c427853a7fe1756c13b185276163903007f0e1e3763fe51667d293f8892a530ec5b14dbc63722371778ee1ee5b4675690a26fe50e9351c1726b5763d85cfd577c6ba27264fa5855111b475e5535ba2f36f4864b8458b1517cf118856f0c0ae3613877061b2d10e2946694f2f73efcf52afdae320fa51fb1bbc8c98febe687343af44105f2e870c24ffda2c2b6adfa9e3875e243b88517effde3dbca1857890d0d006e09d50578f41fa71461e253d7d81a5f568f0bba788daf55509c2d2bd827cea21f7f3a3f7eaeba95db7a5b0d954a1ca32a6bd10387cf661272f403095262d0843b2243035cc172c0d31fe5b603fc65637cc70dc26fd40bde7fd01b5f3007ebd9760843fd8d02d523884537d4996b5fa83f6ca275093b7c71e7ece2c9f49aee9ded0edcca5ea131681b3f79a1e1a82b2ff0f2c7898f7c51985f59e17304c0798173bd5b76c8a32c619ceb329c5178e341bb44d0dbf11738d9c2ebc07b5016a87cb63eab68f1c1ea86c304eac1f54bf97df49b26f4d50bf815ffaaeb43e5f7c12bc76aca6d3738e3d2d39ba43ddad8989418b618c587e633f4a997d881d1273fe4aeea3c7890349e2e74bdd2c40fca190e1289f0ce4f5ca5957155ab1a2617c6665e8ef08fcec622333adef8c285b797553eba9c76ec8d8f5b9a955bc40b9244e18d2fc52bdb06d1182b6a885d406602bcfc2b988c667e5696ed7cce33f209825f68f383b38ce7142a7c0f26e61e8631f42cab247c17c7562fe2fcc5c9854351dcb39636ac3cd3c9b1aebc761a9c0dcff3be9d6f99b1468fc575e9756bc7a91f69f401a37a6d1dca2971cb1774fc40be34749ca6179e11f0c0d18aaa9265cb9ee1511a78986cf40301af2f1d2eb6195e9e65728d2d3e48e8627ec241ba3510009867446a3704d4b02e26338300c104993f05bf02849202f89fec7231f6156774360134e04f19b050b454e52a7bdf91ac8eda64b729ca71ce15ef3e78a71455ac0a11db457843a147abfa5e190e7bfbed773d5dfc998e583d3b1fd6b9d524b95581ca56a7c6277e61d113dcd29f58851d5ebc5b2c88b41ffab318985ce3b2caa3bc3a16d6fc2ded3d0c0f84fb68287e88e7891f9bc72629892472278e1587e103a11ed9d3b7f27b8000708906a5f2aa73f56afee4646f8c3f475b4502b800485bd3d4c5c8b20cd283775351a23febed54afba558796594a332a74de6a30c612aa87337aa2323f3831d99bc886a2185a2c05374a1d81324defd2a420433062dbc12871de25e42e414494675dc64775325d64f4e20728589b455e2dc55155f0c2eb816cf0434727206047fe48f72fb0e2c711a72cd2f93b7538f177da626e74ad4c4a4efa60febf5feb9152dec71318726f8260b5a470f04a9fd294acf4d50d2a5e5c57a2dc26160df4e6211d9abefae18e7ef0164fb35d72cde510903c4d3d2331455184f6c4ff45aa12d26e36be149338757e8b3d048ae07386f2b48c6b2162a5f8bdd3c5b322422c56d5cab1c494fb2911709516f3b363593e86dad810bb22c5c84331f8fcab37514e5b8cfe24813eac8375f93e3e0785b3e35659a8605ed330aee996139f015184543229e972d8959dbc2d6bf3cd6b1fc1cf6e6778411f071125fdfe7175b0066303e933b4e19567cbc85396261628b005471185fbe8823dd7c12b2c3e29df711485a77f4a5af82aa31ee3d741b9956f6715191909a78d4d977e21b921092a229fa81f0478e003ffc316ab1cce24d3ba82c3332ed7ad99a8c4bca6ed85b54a0e9b47c68a4e5f295d7faff22622fefe4e761a18b5a39ce546e331427009297ed3ab42ad2717541ea5c19fb4e95004a5ad5070d239055096737a7c228e87d4eff877cbb226039e861d8b96cc1a3871756b0c38b4712a7068db71f37e57f29c55cc2b33565294b7716586de061c3ac52f270eb24f3dce346584c24af7503ed8c1a78ef0e94334c0e1d3f253ce4d9a7a98d07a5ff9298347f9d4ffd49a6b2ed5a71cd4546be422bb7f14b8515b6311805321f019a6a53d8ac233c44ba035168fc775a7e69ff9c34330421ef92074a400267b4aa5688a297472e8e17106db5f5e2aa8be27ce99a63cef93013d088d5421821ff2ade00684dd767a3097e0d829f4b95b1dc6b91aa6dcde0b2b5fd9fcc8ef953da9df727efad133cf3e23fd346dd4b63e01c7554665bbe072290ee09a17b3d033c2439f86b3f15b0625e93450ca835f3b552a86a5bbea72cd9bdbf8b565a5a5ace18415af65f7c65231cd7a976e5d0383cfc9776326657c256df9e83839fb0e78c1e5ed9631ae4c7d0ab25d7b23225e14c8e5ca4d855c755af60a6fa56f38450fd8248ce5c3d7700b535df9744bf10146bc3f6de1b460328a12aed8533fa067280287b63eb45eb405f3c09468d372701b80ddf939db169e71584491a8390285644123e4a439055a290a61349ae5b447f8b4885b1cacd8f878e2c9a7d4b99e7bf4b8b6f13ff0dc07f59a0bf05f79c4ef40331231f2d438d38f0e270cb86f9b92990682f0a60fab5478a55b3a8762e8421f230095878b6f2ea7fdf02ebfab9c12b59cfcfbe13c545e5e677ccabe4f4661a996a9a089caa738d85dd9e68baee3127738089a3f6c066c96e53b65d5ad60e395aeae86e58900121d03d81952c1814c99cfc1ea33724b8e782b636582321d65648527194a29a383214e30e8262c1f4c9e72608517aff00777172d6a188f0e3782c208c66549713265de122a203c4e65876be9c95a51b6ef957c5c0fa2f50dc6a79e79d65e8ec3383c9c759a0d17df1887a432223dd2309eec845bbd46becaaa3768e11b58d3c0c169fec43fd3e0359cf0d2b5ec1a535efce29e30f09b3fcbaf7cc8c3a73867fa8a5b1c11c03c2e9b1ee3aa108171a89321ae8e8bd790427f2d5bfce1e3fad872fc815267a79e16c39179c8830f6addf0a8cec42eb9955ffdd1899d69c2d20f122c72bca20de21ca430d091d8d60fae7d05f9114e3b015b5feb25da89ef74b213e9e0759bbcc7a91550c4c5e770d0e9d2918f8f56b041f2c963fa0cb5eb5d48369493f92688d9f688a80c1afac853554c3efc77be42965e02906e80ff993fd3e09cf9335d4796cfdf263ee9aff8e5d7185cd2fc5de98a038fe2ddc78fb287ca556092f09af4768bfd0fc229eb2a83bc75b3d3214ebd2d3889b3a20ffe2e7ae0629e2d130decadae628b2ad766ac2d9d837682c4f8ce19cb8f0fcba7e58e4b35bce001bc65d5e5f44361e19f2be5c04fbcff0b6812f2e14af2660e0000000049454e44ae426082", + expectedOutput: "https://globeon.mobi/jyri", + recipeConfig: [ + { + "op": "From Hex", + "args": ["Space"] + }, + { + "op": "Parse QR Code", + "args": [true] + } + ] + } +]); From 0046f7e3d763742619f0be5dafe055e99b599d33 Mon Sep 17 00:00:00 2001 From: Matt C Date: Fri, 21 Dec 2018 15:57:09 +0000 Subject: [PATCH 022/147] Added colour channel splitting support --- package-lock.json | 705 +++++++++++++++++--- src/core/Utils.mjs | 15 +- src/core/operations/SplitColourChannels.mjs | 112 ++++ 3 files changed, 742 insertions(+), 90 deletions(-) create mode 100644 src/core/operations/SplitColourChannels.mjs diff --git a/package-lock.json b/package-lock.json index f146cc0f..170bdbc9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -701,6 +701,22 @@ "regexpu-core": "^4.1.3" } }, + "@babel/polyfill": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.2.3.tgz", + "integrity": "sha512-XG854errfwBkrnWV9dDmSSa8ueTciFUMcC47Mn9obkfLipRKaQL4o33YRsNk0IEep1WXMvr1hGz8DQbSQC2tPQ==", + "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/preset-env": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.1.6.tgz", @@ -820,6 +836,296 @@ } } }, + "@jimp/bmp": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.6.0.tgz", + "integrity": "sha512-zZOcVT1zK/1QL5a7qirkzPPgDKB1ianER7pBdpR2J71vx/g8MnrPbL3h/jEVPxjdci2Hph/VWhc/oLBtTbqO8w==", + "requires": { + "@jimp/utils": "^0.6.0", + "bmp-js": "^0.1.0", + "core-js": "^2.5.7" + } + }, + "@jimp/core": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.6.0.tgz", + "integrity": "sha512-ngAkyCLtX7buc2QyFy0ql/j4R2wGYQVsVhW2G3Y0GVAAklRIFIUYpyNKrqs228xA8f2O6XStbDStFlYkt7uNeg==", + "requires": { + "@jimp/utils": "^0.6.0", + "any-base": "^1.1.0", + "buffer": "^5.2.0", + "core-js": "^2.5.7", + "exif-parser": "^0.1.12", + "file-type": "^9.0.0", + "load-bmfont": "^1.3.1", + "mkdirp": "0.5.1", + "phin": "^2.9.1", + "pixelmatch": "^4.0.2", + "tinycolor2": "^1.4.1" + }, + "dependencies": { + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + } + } + }, + "@jimp/custom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.6.0.tgz", + "integrity": "sha512-+YZIWhf03Rfbi+VPbHomKInu3tcntF/aij/JrIJd1QZq13f8m3mRNxakXupiL18KH0C8BPNDk8RiwFX+HaOw3A==", + "requires": { + "@jimp/core": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/gif": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.6.0.tgz", + "integrity": "sha512-aWQ02P0ymTN1eh0BVsY+84wMdb/QeiVpCNQZl9y50cRnpuMM8TTmF/ZdCEBDiTRFcwXzHsqBXcLwEcYp3X2lTw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7", + "omggif": "^1.0.9" + } + }, + "@jimp/jpeg": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.6.0.tgz", + "integrity": "sha512-quYb+lM4h57jQvr2q9dEIkc0laTljws4dunIdFhJRfa5UlNL5mHInk8h5MxyALo0mZdT07TAcxiDHw5QXZ28JQ==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7", + "jpeg-js": "^0.3.4" + } + }, + "@jimp/plugin-blit": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.6.0.tgz", + "integrity": "sha512-LjiCa+8OT2fgmvBpZt0ogurg/eu5kB8ZFWDRwHPcf8i+058sZC20dar/qrjVd5Knssq4ynjb5oAHsGuJq16Rqw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-blur": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.6.0.tgz", + "integrity": "sha512-/vjGcEiHda6OLTCYqXPFkfSTbL+RatZoGcp1vewcWqChUccn9QVINTlxB7nEI/3Nb/i7KdhOPNEQh1k6q6QXsw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-color": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.6.0.tgz", + "integrity": "sha512-mvDeAwN8ZpDkOaABMJ0w9zUzo9OOtu1qvvPkSirXDTMiXt1nsbfz8BoeoD7nU2MFhQj5MiGjH65UDnsH5ZzYuw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7", + "tinycolor2": "^1.4.1" + } + }, + "@jimp/plugin-contain": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.6.0.tgz", + "integrity": "sha512-gPHnoQkDztMbvnTVo01BaMoM/hhDJdeJ7FRToD4p4Qvdor4V0I6NXtjOeUPXfD94miTgh/UTyJDqeG4GZzi4sA==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-cover": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.6.0.tgz", + "integrity": "sha512-iv9lA2v3qv+x3eaTThtyzFg+hO8/pSnM8NBymC5OlpSJnR54aWi7BVFXLJAF27T4EZyXko432PVul2IdY3BEPw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-crop": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.6.0.tgz", + "integrity": "sha512-YftdmFZ2YnZDYyBulkStCt2MZbKKfbjytkE+6i3Djk2b/Rfryg5xjgzVnAumCRQJhVPukexrnc2V7KKbEgx7mQ==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-displace": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.6.0.tgz", + "integrity": "sha512-kkva5Fy3r7J7QmiqYQ5c9NeUKKkN7+KSfCGsZ6tkRHK4REMIXhQO/OnJN8XG6RReV29O6QykdyeTXDiHUDiROw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-dither": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.6.0.tgz", + "integrity": "sha512-ILSG7bl3SOqmcIa9C4nBvs0h0E0ObnMbeKWUZiNuz6i0OAlbxryiIfU4j0UVQD5XqT9ksC5mviVNrvOMw4SZLw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-flip": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.6.0.tgz", + "integrity": "sha512-MXGGwABjERvfqVadEzJuVAmbsEQfjxXD0O/mMBegU1Qh7/JmnKAVplQCnojsMPxUdao/FKZjQqOnB/j4LLJtOQ==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-gaussian": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.6.0.tgz", + "integrity": "sha512-RUsBCyj6Ukxgn/TU8v6c6WRbSFqKM0iknLVqDkKIuiOyJB7ougv66fqomh/i/h3ihIkEnf50BuO0c3ovrczfvw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-invert": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.6.0.tgz", + "integrity": "sha512-zTCqK8el6eqcNKAxw0y57gHBFgxygI5iM8dQDPyqsvVWO71i8XII7ubnJhEvPPN7vhIKlOSnS9XXglezvJoX4Q==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-mask": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.6.0.tgz", + "integrity": "sha512-zkZVqAA7lxWhkn5EbPjBQ6tPluYIGfLMSX4kD1gksj+MVJJnVAd459AVuEXCvkUvv4wG5AlH8m6ve5NZj9vvxw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-normalize": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.6.0.tgz", + "integrity": "sha512-7bNGT+S0rw9gvmxpkNsA19JSqBZYFrAn9QhEmoN4HIimdKtJaoLJh/GnxrPuOBLuv1IPJntoTOOWvOmfrQ6/ww==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-print": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.6.0.tgz", + "integrity": "sha512-kXNHYo7bGQiMZkUqhCvm6OomjJtZnLGs7cgXp9qsCfPcDBLLW+X3oxnoLaePQMlpQt6hX/lzFnNaWKv/KB1jlA==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7", + "load-bmfont": "^1.4.0" + } + }, + "@jimp/plugin-resize": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.6.0.tgz", + "integrity": "sha512-m0AA/mPkJG++RuftBFDUMRenqgIN/uSh88Kqs33VURYaabApni4ML3QslE1TCJtl2Lnu1eosxYlbzODjHx49eg==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-rotate": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.6.0.tgz", + "integrity": "sha512-1QGlIisyxs2HNLuynq/ETc4h7E6At3yR+IYAhG9U4KONG4RqlIy0giyDhnfEZaiqOE+O7f+0Z7zN6GoSHmQjzg==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-scale": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.6.0.tgz", + "integrity": "sha512-le/ttYwYioNPRoMlMaoJMCTv+m8d1v0peo/3J8E6Rf9ok7Bw3agkvjL9ILnsmr8jXj1YLrBSPKRs5nJ6ziM/qA==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugins": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.6.0.tgz", + "integrity": "sha512-9+znfBJM1B31kvw+IcQFnAuDntQhwca/SONFnKOSZ8BNiQdiuTNbXHFxOo3tvdv1ngtB+LkkiTgK+QoF358b8g==", + "requires": { + "@jimp/plugin-blit": "^0.6.0", + "@jimp/plugin-blur": "^0.6.0", + "@jimp/plugin-color": "^0.6.0", + "@jimp/plugin-contain": "^0.6.0", + "@jimp/plugin-cover": "^0.6.0", + "@jimp/plugin-crop": "^0.6.0", + "@jimp/plugin-displace": "^0.6.0", + "@jimp/plugin-dither": "^0.6.0", + "@jimp/plugin-flip": "^0.6.0", + "@jimp/plugin-gaussian": "^0.6.0", + "@jimp/plugin-invert": "^0.6.0", + "@jimp/plugin-mask": "^0.6.0", + "@jimp/plugin-normalize": "^0.6.0", + "@jimp/plugin-print": "^0.6.0", + "@jimp/plugin-resize": "^0.6.0", + "@jimp/plugin-rotate": "^0.6.0", + "@jimp/plugin-scale": "^0.6.0", + "core-js": "^2.5.7", + "timm": "^1.6.1" + } + }, + "@jimp/png": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.6.0.tgz", + "integrity": "sha512-DBtMyQyrJxuKI7/1dVqLek+rCMM8U6BSOTHgo05wU7lhJKTB6fn2tbYfsnHQKzd9ld1M2qKuC+O1GTVdB2yl6w==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7", + "pngjs": "^3.3.3" + } + }, + "@jimp/tiff": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.6.0.tgz", + "integrity": "sha512-PV95CquEsolFziq0zZrAEJIzZSKwMK89TvkOXTPDi/xesgdXGC2rtG1IZFpC9L4UX5hi/M5GaeJa49xULX6Nqw==", + "requires": { + "core-js": "^2.5.7", + "utif": "^2.0.1" + } + }, + "@jimp/types": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.6.0.tgz", + "integrity": "sha512-j4tm82huEWpLrwave/2NYnMTY6us/6K9Js6Vd/CHoM/ki8M71tMXEVzc8tly92wtnEzQ9+FEk0Ue6pYo68m/5A==", + "requires": { + "@jimp/bmp": "^0.6.0", + "@jimp/gif": "^0.6.0", + "@jimp/jpeg": "^0.6.0", + "@jimp/png": "^0.6.0", + "@jimp/tiff": "^0.6.0", + "core-js": "^2.5.7", + "timm": "^1.6.1" + } + }, + "@jimp/utils": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.6.0.tgz", + "integrity": "sha512-z5iYEfqc45vlYweROneNkjv32en6jS7lPL/eMLIvaEcQAHaoza20Dw8fUoJ0Ht9S92kR74xeTunAZq+gK2w67Q==", + "requires": { + "core-js": "^2.5.7" + } + }, "@webassemblyjs/ast": { "version": "1.7.11", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz", @@ -1171,7 +1477,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1191,6 +1497,11 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, + "any-base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==" + }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -1284,7 +1595,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -1369,7 +1680,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1457,7 +1768,7 @@ }, "axios": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "dev": true, "requires": { @@ -1638,8 +1949,7 @@ "base64-js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", - "dev": true + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" }, "batch": { "version": "0.6.1", @@ -1701,6 +2011,11 @@ "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", "dev": true }, + "bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha1-4Fpj95amwf8l9Hcex62twUjAcjM=" + }, "bn": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bn/-/bn-1.0.1.tgz", @@ -1863,7 +2178,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -1900,7 +2215,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -1950,7 +2265,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -1967,6 +2282,11 @@ } } }, + "buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=" + }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -2015,7 +2335,7 @@ }, "cacache": { "version": "10.0.4", - "resolved": "http://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", "dev": true, "requires": { @@ -2092,7 +2412,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -2123,7 +2443,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -2590,7 +2910,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -2603,7 +2923,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -2721,7 +3041,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -3055,7 +3375,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -3119,7 +3439,7 @@ "dependencies": { "domelementtype": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", "dev": true }, @@ -3131,6 +3451,11 @@ } } }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" + }, "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", @@ -3307,7 +3632,7 @@ }, "entities": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", "dev": true }, @@ -3731,7 +4056,7 @@ }, "eventemitter2": { "version": "0.4.14", - "resolved": "http://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", "dev": true }, @@ -3743,7 +4068,7 @@ }, "events": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "dev": true }, @@ -4124,6 +4449,11 @@ "integrity": "sha1-peeo/7+kk7Q7kju9TKiaU7Y7YSs=", "dev": true }, + "file-type": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz", + "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==" + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -4149,7 +4479,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -4280,6 +4610,14 @@ } } }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -4377,7 +4715,7 @@ }, "fs-extra": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", "dev": true, "requires": { @@ -4445,12 +4783,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4465,17 +4805,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -4592,7 +4935,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -4604,6 +4948,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4618,6 +4963,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4625,12 +4971,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -4649,6 +4997,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -4729,7 +5078,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -4741,6 +5091,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -4862,6 +5213,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5023,7 +5375,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -5083,6 +5435,22 @@ } } }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "requires": { + "min-document": "^2.19.0", + "process": "~0.5.1" + }, + "dependencies": { + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + } + } + }, "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", @@ -5173,7 +5541,7 @@ }, "grunt-cli": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", "dev": true, "requires": { @@ -5221,7 +5589,7 @@ "dependencies": { "shelljs": { "version": "0.5.3", - "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", "integrity": "sha1-xUmCuZbHbvDB5rWfvcWCX1txMRM=", "dev": true } @@ -5241,7 +5609,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -5269,7 +5637,7 @@ }, "grunt-contrib-jshint": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", "integrity": "sha1-Np2QmyWTxA6L55lAshNAhQx5Oaw=", "dev": true, "requires": { @@ -5368,7 +5736,7 @@ "dependencies": { "colors": { "version": "1.1.2", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true } @@ -5432,7 +5800,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -5450,7 +5818,7 @@ }, "handle-thing": { "version": "1.2.5", - "resolved": "http://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", "dev": true }, @@ -5677,7 +6045,7 @@ }, "html-webpack-plugin": { "version": "3.2.0", - "resolved": "http://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", "dev": true, "requires": { @@ -5725,7 +6093,7 @@ }, "htmlparser2": { "version": "3.8.3", - "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", "dev": true, "requires": { @@ -5744,7 +6112,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -5773,7 +6141,7 @@ }, "http-proxy-middleware": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { @@ -5893,8 +6261,7 @@ "ieee754": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", - "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", - "dev": true + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==" }, "iferr": { "version": "0.1.5", @@ -6225,7 +6592,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -6235,8 +6602,7 @@ "is-callable": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" }, "is-data-descriptor": { "version": "0.1.4", @@ -6316,6 +6682,11 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=" + }, "is-glob": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", @@ -6462,6 +6833,18 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, + "jimp": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.6.0.tgz", + "integrity": "sha512-RYpN+AAlTEMf8Bnkhq2eeTNyr70rDK/2UUfUqzBJmwmZwdR6fxRJvgbCGWT1BDVRxaAqo+4CWm8ePBxOIsr4jg==", + "requires": { + "@babel/polyfill": "^7.0.0", + "@jimp/custom": "^0.6.0", + "@jimp/plugins": "^0.6.0", + "@jimp/types": "^0.6.0", + "core-js": "^2.5.7" + } + }, "jison": { "version": "0.4.13", "resolved": "https://registry.npmjs.org/jison/-/jison-0.4.13.tgz", @@ -6508,6 +6891,11 @@ "nomnom": "1.5.2" } }, + "jpeg-js": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.4.tgz", + "integrity": "sha512-6IzjQxvnlT8UlklNmDXIJMWxijULjqGrzgqc0OG7YadZdvm7KPQ1j0ehmQQHckgEWOfgpptzcnWgESovxudpTA==" + }, "jquery": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", @@ -6750,7 +7138,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -6810,6 +7198,11 @@ "verror": "1.10.0" } }, + "jsqr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/jsqr/-/jsqr-1.1.1.tgz", + "integrity": "sha512-FVoMU2ncTyjaOqN/vwvDnZ7jaAVvFzM3LK3vG3jvQZFWJQlAwJ1XTCOgAEKo+4Rkd6ydMXTTvqGV/4w5VunmTw==" + }, "jsrsasign": { "version": "8.0.12", "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.12.tgz", @@ -6856,7 +7249,7 @@ }, "kew": { "version": "0.7.0", - "resolved": "http://registry.npmjs.org/kew/-/kew-0.7.0.tgz", + "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", "dev": true }, @@ -6934,6 +7327,28 @@ "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", "dev": true }, + "load-bmfont": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.0.tgz", + "integrity": "sha512-kT63aTAlNhZARowaNYcY29Fn/QYkc52M3l6V1ifRcPewg2lvUZDAj7R6dXjOL9D0sict76op3T5+odumDSF81g==", + "requires": { + "buffer-equal": "0.0.1", + "mime": "^1.3.4", + "parse-bmfont-ascii": "^1.0.3", + "parse-bmfont-binary": "^1.0.5", + "parse-bmfont-xml": "^1.1.4", + "phin": "^2.9.1", + "xhr": "^2.0.1", + "xtend": "^4.0.0" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + } + } + }, "load-grunt-tasks": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-3.5.2.tgz", @@ -6948,7 +7363,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -6961,7 +7376,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -7196,7 +7611,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -7255,7 +7670,7 @@ }, "meow": { "version": "3.7.0", - "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { @@ -7347,6 +7762,14 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -7432,9 +7855,8 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, "requires": { "minimist": "0.0.8" }, @@ -7442,8 +7864,7 @@ "minimist": { "version": "0.0.8", "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } }, @@ -7554,7 +7975,7 @@ }, "ncp": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=", "dev": true }, @@ -7756,7 +8177,7 @@ "dependencies": { "colors": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" }, "underscore": { @@ -7943,6 +8364,11 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, + "omggif": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.9.tgz", + "integrity": "sha1-3LcCTazVDFK00wPwSALJHAV8dl8=" + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -8015,13 +8441,13 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-locale": { "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -8030,7 +8456,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -8116,8 +8542,7 @@ "pako": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", - "dev": true + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" }, "parallel-transform": { "version": "1.1.0", @@ -8173,7 +8598,7 @@ }, "parse-asn1": { "version": "5.1.1", - "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", "dev": true, "requires": { @@ -8184,6 +8609,34 @@ "pbkdf2": "^3.0.3" } }, + "parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU=" + }, + "parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY=" + }, + "parse-bmfont-xml": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz", + "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==", + "requires": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.4.5" + } + }, + "parse-headers": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.1.tgz", + "integrity": "sha1-aug6eqJanZtwCswoaYzR8e1+lTY=", + "requires": { + "for-each": "^0.3.2", + "trim": "0.0.1" + } + }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -8231,7 +8684,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -8272,7 +8725,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -8341,6 +8794,11 @@ "which": "^1.2.10" } }, + "phin": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", + "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==" + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -8362,6 +8820,14 @@ "pinkie": "^2.0.0" } }, + "pixelmatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", + "integrity": "sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=", + "requires": { + "pngjs": "^3.0.0" + } + }, "pkg-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", @@ -8419,6 +8885,11 @@ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "dev": true }, + "pngjs": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.3.3.tgz", + "integrity": "sha512-1n3Z4p3IOxArEs1VRXnZ/RXdfEniAUS9jb68g58FIXMNkPJeZd+Qh4Uq7/e0LVxAQGos1eIUrqrt4FpjdnEd+Q==" + }, "popper.js": { "version": "1.14.5", "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.5.tgz", @@ -8839,7 +9310,7 @@ }, "progress": { "version": "1.1.8", - "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=" }, "promise-inflight": { @@ -8864,13 +9335,13 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true }, "winston": { "version": "2.1.1", - "resolved": "http://registry.npmjs.org/winston/-/winston-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", "dev": true, "requires": { @@ -8885,7 +9356,7 @@ "dependencies": { "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true }, @@ -8973,6 +9444,11 @@ "resolved": "https://registry.npmjs.org/purepack/-/purepack-1.0.4.tgz", "integrity": "sha1-CGKC/ZOShfWGZLqam7oxzbFlzNI=" }, + "qr-image": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/qr-image/-/qr-image-3.2.0.tgz", + "integrity": "sha1-n6gpW+rlDEoUnPn5CaHbRkqGcug=" + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -9064,7 +9540,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -9426,7 +9902,7 @@ }, "require-uncached": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { @@ -9593,7 +10069,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -9741,8 +10217,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "schema-utils": { "version": "0.4.7", @@ -9914,7 +10389,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -9958,7 +10433,7 @@ }, "shelljs": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", "dev": true }, @@ -10610,7 +11085,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -10627,7 +11102,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -10706,7 +11181,7 @@ }, "tar": { "version": "2.2.1", - "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, "requires": { @@ -10734,7 +11209,7 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -10795,6 +11270,11 @@ "setimmediate": "^1.0.4" } }, + "timm": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/timm/-/timm-1.6.1.tgz", + "integrity": "sha512-hqDTYi/bWuDxL2i6T3v6nrvkAQ/1Bc060GSkVEQZp02zTSTB4CHSKsOkliequCftQaNRcjRqUZmpGWs5FfhrNg==" + }, "tiny-lr": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", @@ -10826,6 +11306,11 @@ } } }, + "tinycolor2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", + "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -10921,6 +11406,11 @@ "punycode": "^2.1.0" } }, + "trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" + }, "trim-newlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", @@ -11334,6 +11824,14 @@ "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" }, + "utif": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz", + "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==", + "requires": { + "pako": "^1.0.5" + } + }, "util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", @@ -11381,7 +11879,7 @@ "dependencies": { "async": { "version": "0.9.2", - "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -11407,7 +11905,7 @@ }, "valid-data-url": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", "integrity": "sha512-FXg2qXMzfAhZc0y2HzELNfUeiOjPr+52hU1DNBWiJJ2luXD+dD1R9NA48Ug5aj0ibbxroeGDc/RJv6ThiGgkDw==", "dev": true }, @@ -11423,7 +11921,7 @@ }, "validator": { "version": "9.4.1", - "resolved": "http://registry.npmjs.org/validator/-/validator-9.4.1.tgz", + "resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz", "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA==", "dev": true }, @@ -11847,7 +12345,7 @@ }, "webpack-node-externals": { "version": "1.7.2", - "resolved": "http://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz", "integrity": "sha512-ajerHZ+BJKeCLviLUUmnyd5B4RavLF76uv3cs6KNuO8W+HuQaEs0y0L7o40NQxdPy5w0pcv8Ew7yPUAQG0UdCg==", "dev": true }, @@ -11984,7 +12482,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -12038,12 +12536,44 @@ "async-limiter": "~1.0.0" } }, + "xhr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz", + "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==", + "requires": { + "global": "~4.3.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha1-qQKekp09vN7RafPG4oI42VpdWig=" + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + }, + "dependencies": { + "xmlbuilder": { + "version": "9.0.7", + "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + } + } + }, "xmlbuilder": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz", @@ -12074,8 +12604,7 @@ "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, "y18n": { "version": "3.2.1", diff --git a/src/core/Utils.mjs b/src/core/Utils.mjs index 342d6d6c..7d38a928 100755 --- a/src/core/Utils.mjs +++ b/src/core/Utils.mjs @@ -6,7 +6,7 @@ import utf8 from "utf8"; import moment from "moment-timezone"; -import {fromBase64} from "./lib/Base64"; +import {fromBase64, toBase64} from "./lib/Base64"; import {fromHex} from "./lib/Hex"; import {fromDecimal} from "./lib/Decimal"; import {fromBinary} from "./lib/Binary"; @@ -850,6 +850,17 @@ class Utils { return html; }; + const formatContent = function (buff, type) { + if (type.startsWith("image")) { + let dataURI = "data:"; + dataURI += type + ";"; + dataURI += "base64," + toBase64(buff); + return ""; + } else { + return `
${Utils.escapeHtml(Utils.arrayBufferToStr(buff.buffer))}
`; + } + }; + const formatFile = async function(file, i) { const buff = await Utils.readFile(file); const blob = new Blob( @@ -879,7 +890,7 @@ class Utils {
-
${Utils.escapeHtml(Utils.arrayBufferToStr(buff.buffer))}
+ ${formatContent(buff, file.type)}
`; diff --git a/src/core/operations/SplitColourChannels.mjs b/src/core/operations/SplitColourChannels.mjs new file mode 100644 index 00000000..98804462 --- /dev/null +++ b/src/core/operations/SplitColourChannels.mjs @@ -0,0 +1,112 @@ +/** + * @author Matt C [matt@artemisbot.uk] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import Utils from "../Utils"; +import Magic from "../lib/Magic"; + +import jimp from "jimp"; + +/** + * Split Colour Channels operation + */ +class SplitColourChannels extends Operation { + + /** + * SplitColourChannels constructor + */ + constructor() { + super(); + + this.name = "Split Colour Channels"; + this.module = "Image"; + this.description = "Splits given image into its red, green and blue colour channels."; + this.infoURL = "https://en.wikipedia.org/wiki/Channel_(digital_image)"; + this.inputType = "byteArray"; + this.outputType = "List"; + this.presentType = "html"; + this.args = [ + /* Example arguments. See the project wiki for full details. + { + name: "First arg", + type: "string", + value: "Don't Panic" + }, + { + name: "Second arg", + type: "number", + value: 42 + } + */ + ]; + } + + /** + * @param {byteArray} input + * @param {Object[]} args + * @returns {List} + */ + async run(input, args) { + const type = Magic.magicFileType(input); + // Make sure that the input is an image + if (type && type.mime.indexOf("image") === 0) { + const parsedImage = await jimp.read(Buffer.from(input)); + const red = new Promise(async (resolve, reject) => { + try { + const split = parsedImage.clone() + .color([ + {apply: "blue", params: [-255]}, + {apply: "green", params: [-255]} + ]).getBufferAsync(jimp.MIME_PNG); + resolve(new File([new Uint8Array((await split).values())], "red.png", {type: "image/png"})); + } catch (err) { + reject(new OperationError("Could not split red channel.")); + } + }); + const green = new Promise(async (resolve, reject) => { + try { + const split = parsedImage.clone() + .color([ + {apply: "red", params: [-255]}, + {apply: "blue", params: [-255]}, + ]).getBufferAsync(jimp.MIME_PNG); + resolve(new File([new Uint8Array((await split).values())], "green.png", {type: "image/png"})); + } catch (err) { + reject(new OperationError("Could not split green channel.")); + } + }); + const blue = new Promise(async (resolve, reject) => { + try { + const split = parsedImage + .color([ + {apply: "red", params: [-255]}, + {apply: "green", params: [-255]}, + ]).getBufferAsync(jimp.MIME_PNG); + resolve(new File([new Uint8Array((await split).values())], "blue.png", {type: "image/png"})); + } catch (err) { + reject(new OperationError("Could not split blue channel.")); + } + }); + return await Promise.all([red, green, blue]); + } else { + throw new OperationError("Invalid file type."); + } + } + + /** + * Displays the files in HTML for web apps. + * + * @param {File[]} files + * @returns {html} + */ + async present(files) { + return await Utils.displayFilesAsHTML(files); + } + +} + +export default SplitColourChannels; From 5a9583c9701ebb7059f01a92bb636e58462f714b Mon Sep 17 00:00:00 2001 From: Matt C Date: Fri, 21 Dec 2018 17:15:30 +0000 Subject: [PATCH 023/147] Add to categories --- src/core/config/Categories.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 2e6bfe2d..cfc0a9de 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -364,6 +364,10 @@ "Generate TOTP", "Generate HOTP", "Haversine distance", + "Render Image", + "Remove EXIF", + "Extract EXIF", + "Split Colour Channels", "Numberwang", "XKCD Random Number" ] From 18693d2471876a6e7f118f344772fbde9234396d Mon Sep 17 00:00:00 2001 From: Matt C Date: Fri, 21 Dec 2018 17:17:11 +0000 Subject: [PATCH 024/147] Add tests, however non-functional due to lack of File in Node Also add jimp to package.json --- package-lock.json | 43 ++++++------------- package.json | 1 + src/core/Utils.mjs | 2 +- src/core/operations/SplitColourChannels.mjs | 6 +-- test/index.mjs | 1 + test/tests/operations/SplitColourChannels.mjs | 26 +++++++++++ 6 files changed, 44 insertions(+), 35 deletions(-) create mode 100644 test/tests/operations/SplitColourChannels.mjs diff --git a/package-lock.json b/package-lock.json index 170bdbc9..6b1fd94b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4805,8 +4805,7 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", @@ -4817,8 +4816,7 @@ "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -4948,7 +4946,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4971,14 +4968,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -4997,7 +4992,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5078,8 +5072,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5091,7 +5084,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5213,7 +5205,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5471,7 +5462,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -7198,11 +7189,6 @@ "verror": "1.10.0" } }, - "jsqr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/jsqr/-/jsqr-1.1.1.tgz", - "integrity": "sha512-FVoMU2ncTyjaOqN/vwvDnZ7jaAVvFzM3LK3vG3jvQZFWJQlAwJ1XTCOgAEKo+4Rkd6ydMXTTvqGV/4w5VunmTw==" - }, "jsrsasign": { "version": "8.0.12", "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.12.tgz", @@ -8038,7 +8024,7 @@ "dependencies": { "semver": { "version": "5.3.0", - "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", "dev": true } @@ -8908,7 +8894,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -9444,11 +9430,6 @@ "resolved": "https://registry.npmjs.org/purepack/-/purepack-1.0.4.tgz", "integrity": "sha1-CGKC/ZOShfWGZLqam7oxzbFlzNI=" }, - "qr-image": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/qr-image/-/qr-image-3.2.0.tgz", - "integrity": "sha1-n6gpW+rlDEoUnPn5CaHbRkqGcug=" - }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -9729,7 +9710,7 @@ "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -9780,7 +9761,7 @@ }, "htmlparser2": { "version": "3.3.0", - "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", "dev": true, "requires": { @@ -9792,7 +9773,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -12442,14 +12423,14 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true, "optional": true }, "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true, "optional": true diff --git a/package.json b/package.json index cfa83d39..3d9fad1f 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "exif-parser": "^0.1.12", "file-saver": "^2.0.0-rc.4", "highlight.js": "^9.13.1", + "jimp": "^0.6.0", "jquery": "^3.3.1", "js-crc": "^0.2.0", "js-sha3": "^0.8.0", diff --git a/src/core/Utils.mjs b/src/core/Utils.mjs index 7d38a928..08d45b7d 100755 --- a/src/core/Utils.mjs +++ b/src/core/Utils.mjs @@ -855,7 +855,7 @@ class Utils { let dataURI = "data:"; dataURI += type + ";"; dataURI += "base64," + toBase64(buff); - return ""; + return ""; } else { return `
${Utils.escapeHtml(Utils.arrayBufferToStr(buff.buffer))}
`; } diff --git a/src/core/operations/SplitColourChannels.mjs b/src/core/operations/SplitColourChannels.mjs index 98804462..fb3e3d6e 100644 --- a/src/core/operations/SplitColourChannels.mjs +++ b/src/core/operations/SplitColourChannels.mjs @@ -64,7 +64,7 @@ class SplitColourChannels extends Operation { ]).getBufferAsync(jimp.MIME_PNG); resolve(new File([new Uint8Array((await split).values())], "red.png", {type: "image/png"})); } catch (err) { - reject(new OperationError("Could not split red channel.")); + reject(new OperationError(`Could not split red channel: ${err}`)); } }); const green = new Promise(async (resolve, reject) => { @@ -76,7 +76,7 @@ class SplitColourChannels extends Operation { ]).getBufferAsync(jimp.MIME_PNG); resolve(new File([new Uint8Array((await split).values())], "green.png", {type: "image/png"})); } catch (err) { - reject(new OperationError("Could not split green channel.")); + reject(new OperationError(`Could not split green channel: ${err}`)); } }); const blue = new Promise(async (resolve, reject) => { @@ -88,7 +88,7 @@ class SplitColourChannels extends Operation { ]).getBufferAsync(jimp.MIME_PNG); resolve(new File([new Uint8Array((await split).values())], "blue.png", {type: "image/png"})); } catch (err) { - reject(new OperationError("Could not split blue channel.")); + reject(new OperationError(`Could not split blue channel: ${err}`)); } }); return await Promise.all([red, green, blue]); diff --git a/test/index.mjs b/test/index.mjs index c944b765..1f51275f 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -73,6 +73,7 @@ import "./tests/operations/SeqUtils"; import "./tests/operations/SetDifference"; import "./tests/operations/SetIntersection"; import "./tests/operations/SetUnion"; +import "./tests/operations/SplitColourChannels"; import "./tests/operations/StrUtils"; import "./tests/operations/SymmetricDifference"; import "./tests/operations/TextEncodingBruteForce"; diff --git a/test/tests/operations/SplitColourChannels.mjs b/test/tests/operations/SplitColourChannels.mjs new file mode 100644 index 00000000..ce91cde5 --- /dev/null +++ b/test/tests/operations/SplitColourChannels.mjs @@ -0,0 +1,26 @@ +/** + * Colour channel split tests. + * + * @author Matt C matt@artemisbot.uk + * + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +import TestRegister from "../../TestRegister"; +// Base 85 encoded +const testCard = "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"; +const testCardSplit = "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"; + +TestRegister.addTests([{ + name: "Split Colour Channels: Default (JPEG)", + input: testCard, + expectedOutput: testCardSplit, + recipeConfig: [ + { "op": "From Base64", + "args": ["A-Za-z0-9+/=", true] }, + { "op": "Split Colour Channels", + "args": [] }, + { "op": "To Base64", + "args": ["A-Za-z0-9+/="] } + ] +}]); From 454ef0076b7503d22e698b9c928a2c5907b5c4d4 Mon Sep 17 00:00:00 2001 From: Matt C Date: Fri, 21 Dec 2018 17:19:33 +0000 Subject: [PATCH 025/147] Disabled tests --- test/index.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.mjs b/test/index.mjs index 1f51275f..97e00b92 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -73,7 +73,7 @@ import "./tests/operations/SeqUtils"; import "./tests/operations/SetDifference"; import "./tests/operations/SetIntersection"; import "./tests/operations/SetUnion"; -import "./tests/operations/SplitColourChannels"; +//import "./tests/operations/SplitColourChannels"; import "./tests/operations/StrUtils"; import "./tests/operations/SymmetricDifference"; import "./tests/operations/TextEncodingBruteForce"; From bf2454720236d573465d36956aa15ff67edfb3d8 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 25 Dec 2018 19:02:05 +0000 Subject: [PATCH 026/147] The 'option' and 'editableOption' arguments can now specify a 'defaultIndex' to populate, instead of using the first in the provided list. --- src/core/Ingredient.mjs | 2 ++ src/core/Operation.mjs | 1 + src/web/HTMLIngredient.mjs | 7 ++++--- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/Ingredient.mjs b/src/core/Ingredient.mjs index 02359014..dda66b8f 100755 --- a/src/core/Ingredient.mjs +++ b/src/core/Ingredient.mjs @@ -25,6 +25,7 @@ class Ingredient { this.hint = ""; this.toggleValues = []; this.target = null; + this.defaultIndex = 0; if (ingredientConfig) { this._parseConfig(ingredientConfig); @@ -46,6 +47,7 @@ class Ingredient { this.hint = ingredientConfig.hint || false; this.toggleValues = ingredientConfig.toggleValues; this.target = typeof ingredientConfig.target !== "undefined" ? ingredientConfig.target : null; + this.defaultIndex = typeof ingredientConfig.defaultIndex !== "undefined" ? ingredientConfig.defaultIndex : 0; } diff --git a/src/core/Operation.mjs b/src/core/Operation.mjs index 3f5df52f..3f6b3e86 100755 --- a/src/core/Operation.mjs +++ b/src/core/Operation.mjs @@ -181,6 +181,7 @@ class Operation { if (ing.hint) conf.hint = ing.hint; if (ing.disabled) conf.disabled = ing.disabled; if (ing.target) conf.target = ing.target; + if (ing.defaultIndex) conf.defaultIndex = ing.defaultIndex; return conf; }); } diff --git a/src/web/HTMLIngredient.mjs b/src/web/HTMLIngredient.mjs index 13e16e3f..d026e1f3 100755 --- a/src/web/HTMLIngredient.mjs +++ b/src/web/HTMLIngredient.mjs @@ -26,6 +26,7 @@ class HTMLIngredient { this.disabled = config.disabled || false; this.hint = config.hint || false; this.target = config.target; + this.defaultIndex = config.defaultIndex || 0; this.toggleValues = config.toggleValues; this.id = "ing-" + this.app.nextIngId(); } @@ -133,7 +134,7 @@ class HTMLIngredient { } else if ((m = this.value[i].match(/\[\/([a-z0-9 -()^]+)\]/i))) { html += ""; } else { - html += ``; + html += ``; } } html += ` @@ -154,7 +155,7 @@ class HTMLIngredient { } else if ((m = this.value[i].name.match(/\[\/([a-z0-9 -()^]+)\]/i))) { html += ""; } else { - html += ``; + html += ``; } } html += ` @@ -170,7 +171,7 @@ class HTMLIngredient { class="form-control arg inline" id="${this.id}" arg-name="${this.name}" - value="${this.value[0].value}" + value="${this.value[this.defaultIndex].value}" ${this.disabled ? "disabled" : ""}> ${this.hint ? "" + this.hint + "" : ""}
From 387008bd9cb5698e032eaf486cbeadf35f57842d Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 25 Dec 2018 19:02:13 +0000 Subject: [PATCH 027/147] 8.16.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f146cc0f..87ba8281 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.16.0", + "version": "8.16.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index cfa83d39..ebceafdd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.16.0", + "version": "8.16.1", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 9734b78aebae8610bae35d4ec3647c646a8efad3 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 25 Dec 2018 21:54:38 +0000 Subject: [PATCH 028/147] Tidied up QR code operations --- CHANGELOG.md | 6 + package-lock.json | 704 +++++++++++++++++++++---- src/core/operations/GenerateQRCode.mjs | 63 ++- src/core/operations/ParseQRCode.mjs | 38 +- test/tests/operations/ParseQRCode.mjs | 12 +- 5 files changed, 683 insertions(+), 140 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 751ac287..e2f35228 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ 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.17.0] - 2018-12-25 +- 'Generate QR Code' and 'Parse QR Code' operations added [@j433866] | [#448] + ### [8.16.0] - 2018-12-19 - 'Play Media' operation added [@anthony-arnold] | [#446] @@ -79,6 +82,7 @@ All major and minor version changes will be documented in this file. Details of +[8.17.0]: https://github.com/gchq/CyberChef/releases/tag/v8.17.0 [8.16.0]: https://github.com/gchq/CyberChef/releases/tag/v8.16.0 [8.15.0]: https://github.com/gchq/CyberChef/releases/tag/v8.15.0 [8.14.0]: https://github.com/gchq/CyberChef/releases/tag/v8.14.0 @@ -103,6 +107,7 @@ All major and minor version changes will be documented in this file. Details of [@n1474335]: https://github.com/n1474335 [@d98762625]: https://github.com/d98762625 +[@j433866]: https://github.com/j433866 [@GCHQ77703]: https://github.com/GCHQ77703 [@artemisbot]: https://github.com/artemisbot [@picapi]: https://github.com/picapi @@ -144,3 +149,4 @@ All major and minor version changes will be documented in this file. Details of [#441]: https://github.com/gchq/CyberChef/pull/441 [#443]: https://github.com/gchq/CyberChef/pull/443 [#446]: https://github.com/gchq/CyberChef/pull/446 +[#448]: https://github.com/gchq/CyberChef/pull/448 diff --git a/package-lock.json b/package-lock.json index 87ba8281..2985f54f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -701,6 +701,22 @@ "regexpu-core": "^4.1.3" } }, + "@babel/polyfill": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.2.5.tgz", + "integrity": "sha512-8Y/t3MWThtMLYr0YNC/Q76tqN1w30+b0uQMeFUYauG2UGTR19zyUtFrAzT23zNtBxPp+LbE5E/nwV/q/r3y6ug==", + "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/preset-env": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.1.6.tgz", @@ -820,6 +836,296 @@ } } }, + "@jimp/bmp": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.6.0.tgz", + "integrity": "sha512-zZOcVT1zK/1QL5a7qirkzPPgDKB1ianER7pBdpR2J71vx/g8MnrPbL3h/jEVPxjdci2Hph/VWhc/oLBtTbqO8w==", + "requires": { + "@jimp/utils": "^0.6.0", + "bmp-js": "^0.1.0", + "core-js": "^2.5.7" + } + }, + "@jimp/core": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.6.0.tgz", + "integrity": "sha512-ngAkyCLtX7buc2QyFy0ql/j4R2wGYQVsVhW2G3Y0GVAAklRIFIUYpyNKrqs228xA8f2O6XStbDStFlYkt7uNeg==", + "requires": { + "@jimp/utils": "^0.6.0", + "any-base": "^1.1.0", + "buffer": "^5.2.0", + "core-js": "^2.5.7", + "exif-parser": "^0.1.12", + "file-type": "^9.0.0", + "load-bmfont": "^1.3.1", + "mkdirp": "0.5.1", + "phin": "^2.9.1", + "pixelmatch": "^4.0.2", + "tinycolor2": "^1.4.1" + }, + "dependencies": { + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + } + } + }, + "@jimp/custom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.6.0.tgz", + "integrity": "sha512-+YZIWhf03Rfbi+VPbHomKInu3tcntF/aij/JrIJd1QZq13f8m3mRNxakXupiL18KH0C8BPNDk8RiwFX+HaOw3A==", + "requires": { + "@jimp/core": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/gif": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.6.0.tgz", + "integrity": "sha512-aWQ02P0ymTN1eh0BVsY+84wMdb/QeiVpCNQZl9y50cRnpuMM8TTmF/ZdCEBDiTRFcwXzHsqBXcLwEcYp3X2lTw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7", + "omggif": "^1.0.9" + } + }, + "@jimp/jpeg": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.6.0.tgz", + "integrity": "sha512-quYb+lM4h57jQvr2q9dEIkc0laTljws4dunIdFhJRfa5UlNL5mHInk8h5MxyALo0mZdT07TAcxiDHw5QXZ28JQ==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7", + "jpeg-js": "^0.3.4" + } + }, + "@jimp/plugin-blit": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.6.0.tgz", + "integrity": "sha512-LjiCa+8OT2fgmvBpZt0ogurg/eu5kB8ZFWDRwHPcf8i+058sZC20dar/qrjVd5Knssq4ynjb5oAHsGuJq16Rqw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-blur": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.6.0.tgz", + "integrity": "sha512-/vjGcEiHda6OLTCYqXPFkfSTbL+RatZoGcp1vewcWqChUccn9QVINTlxB7nEI/3Nb/i7KdhOPNEQh1k6q6QXsw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-color": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.6.0.tgz", + "integrity": "sha512-mvDeAwN8ZpDkOaABMJ0w9zUzo9OOtu1qvvPkSirXDTMiXt1nsbfz8BoeoD7nU2MFhQj5MiGjH65UDnsH5ZzYuw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7", + "tinycolor2": "^1.4.1" + } + }, + "@jimp/plugin-contain": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.6.0.tgz", + "integrity": "sha512-gPHnoQkDztMbvnTVo01BaMoM/hhDJdeJ7FRToD4p4Qvdor4V0I6NXtjOeUPXfD94miTgh/UTyJDqeG4GZzi4sA==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-cover": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.6.0.tgz", + "integrity": "sha512-iv9lA2v3qv+x3eaTThtyzFg+hO8/pSnM8NBymC5OlpSJnR54aWi7BVFXLJAF27T4EZyXko432PVul2IdY3BEPw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-crop": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.6.0.tgz", + "integrity": "sha512-YftdmFZ2YnZDYyBulkStCt2MZbKKfbjytkE+6i3Djk2b/Rfryg5xjgzVnAumCRQJhVPukexrnc2V7KKbEgx7mQ==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-displace": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.6.0.tgz", + "integrity": "sha512-kkva5Fy3r7J7QmiqYQ5c9NeUKKkN7+KSfCGsZ6tkRHK4REMIXhQO/OnJN8XG6RReV29O6QykdyeTXDiHUDiROw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-dither": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.6.0.tgz", + "integrity": "sha512-ILSG7bl3SOqmcIa9C4nBvs0h0E0ObnMbeKWUZiNuz6i0OAlbxryiIfU4j0UVQD5XqT9ksC5mviVNrvOMw4SZLw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-flip": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.6.0.tgz", + "integrity": "sha512-MXGGwABjERvfqVadEzJuVAmbsEQfjxXD0O/mMBegU1Qh7/JmnKAVplQCnojsMPxUdao/FKZjQqOnB/j4LLJtOQ==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-gaussian": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.6.0.tgz", + "integrity": "sha512-RUsBCyj6Ukxgn/TU8v6c6WRbSFqKM0iknLVqDkKIuiOyJB7ougv66fqomh/i/h3ihIkEnf50BuO0c3ovrczfvw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-invert": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.6.0.tgz", + "integrity": "sha512-zTCqK8el6eqcNKAxw0y57gHBFgxygI5iM8dQDPyqsvVWO71i8XII7ubnJhEvPPN7vhIKlOSnS9XXglezvJoX4Q==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-mask": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.6.0.tgz", + "integrity": "sha512-zkZVqAA7lxWhkn5EbPjBQ6tPluYIGfLMSX4kD1gksj+MVJJnVAd459AVuEXCvkUvv4wG5AlH8m6ve5NZj9vvxw==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-normalize": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.6.0.tgz", + "integrity": "sha512-7bNGT+S0rw9gvmxpkNsA19JSqBZYFrAn9QhEmoN4HIimdKtJaoLJh/GnxrPuOBLuv1IPJntoTOOWvOmfrQ6/ww==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-print": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.6.0.tgz", + "integrity": "sha512-kXNHYo7bGQiMZkUqhCvm6OomjJtZnLGs7cgXp9qsCfPcDBLLW+X3oxnoLaePQMlpQt6hX/lzFnNaWKv/KB1jlA==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7", + "load-bmfont": "^1.4.0" + } + }, + "@jimp/plugin-resize": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.6.0.tgz", + "integrity": "sha512-m0AA/mPkJG++RuftBFDUMRenqgIN/uSh88Kqs33VURYaabApni4ML3QslE1TCJtl2Lnu1eosxYlbzODjHx49eg==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-rotate": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.6.0.tgz", + "integrity": "sha512-1QGlIisyxs2HNLuynq/ETc4h7E6At3yR+IYAhG9U4KONG4RqlIy0giyDhnfEZaiqOE+O7f+0Z7zN6GoSHmQjzg==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugin-scale": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.6.0.tgz", + "integrity": "sha512-le/ttYwYioNPRoMlMaoJMCTv+m8d1v0peo/3J8E6Rf9ok7Bw3agkvjL9ILnsmr8jXj1YLrBSPKRs5nJ6ziM/qA==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7" + } + }, + "@jimp/plugins": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.6.0.tgz", + "integrity": "sha512-9+znfBJM1B31kvw+IcQFnAuDntQhwca/SONFnKOSZ8BNiQdiuTNbXHFxOo3tvdv1ngtB+LkkiTgK+QoF358b8g==", + "requires": { + "@jimp/plugin-blit": "^0.6.0", + "@jimp/plugin-blur": "^0.6.0", + "@jimp/plugin-color": "^0.6.0", + "@jimp/plugin-contain": "^0.6.0", + "@jimp/plugin-cover": "^0.6.0", + "@jimp/plugin-crop": "^0.6.0", + "@jimp/plugin-displace": "^0.6.0", + "@jimp/plugin-dither": "^0.6.0", + "@jimp/plugin-flip": "^0.6.0", + "@jimp/plugin-gaussian": "^0.6.0", + "@jimp/plugin-invert": "^0.6.0", + "@jimp/plugin-mask": "^0.6.0", + "@jimp/plugin-normalize": "^0.6.0", + "@jimp/plugin-print": "^0.6.0", + "@jimp/plugin-resize": "^0.6.0", + "@jimp/plugin-rotate": "^0.6.0", + "@jimp/plugin-scale": "^0.6.0", + "core-js": "^2.5.7", + "timm": "^1.6.1" + } + }, + "@jimp/png": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.6.0.tgz", + "integrity": "sha512-DBtMyQyrJxuKI7/1dVqLek+rCMM8U6BSOTHgo05wU7lhJKTB6fn2tbYfsnHQKzd9ld1M2qKuC+O1GTVdB2yl6w==", + "requires": { + "@jimp/utils": "^0.6.0", + "core-js": "^2.5.7", + "pngjs": "^3.3.3" + } + }, + "@jimp/tiff": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.6.0.tgz", + "integrity": "sha512-PV95CquEsolFziq0zZrAEJIzZSKwMK89TvkOXTPDi/xesgdXGC2rtG1IZFpC9L4UX5hi/M5GaeJa49xULX6Nqw==", + "requires": { + "core-js": "^2.5.7", + "utif": "^2.0.1" + } + }, + "@jimp/types": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.6.0.tgz", + "integrity": "sha512-j4tm82huEWpLrwave/2NYnMTY6us/6K9Js6Vd/CHoM/ki8M71tMXEVzc8tly92wtnEzQ9+FEk0Ue6pYo68m/5A==", + "requires": { + "@jimp/bmp": "^0.6.0", + "@jimp/gif": "^0.6.0", + "@jimp/jpeg": "^0.6.0", + "@jimp/png": "^0.6.0", + "@jimp/tiff": "^0.6.0", + "core-js": "^2.5.7", + "timm": "^1.6.1" + } + }, + "@jimp/utils": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.6.0.tgz", + "integrity": "sha512-z5iYEfqc45vlYweROneNkjv32en6jS7lPL/eMLIvaEcQAHaoza20Dw8fUoJ0Ht9S92kR74xeTunAZq+gK2w67Q==", + "requires": { + "core-js": "^2.5.7" + } + }, "@webassemblyjs/ast": { "version": "1.7.11", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz", @@ -1171,7 +1477,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1191,6 +1497,11 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, + "any-base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==" + }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -1284,7 +1595,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -1369,7 +1680,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1457,7 +1768,7 @@ }, "axios": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "dev": true, "requires": { @@ -1638,8 +1949,7 @@ "base64-js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", - "dev": true + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" }, "batch": { "version": "0.6.1", @@ -1701,6 +2011,11 @@ "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", "dev": true }, + "bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha1-4Fpj95amwf8l9Hcex62twUjAcjM=" + }, "bn": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bn/-/bn-1.0.1.tgz", @@ -1863,7 +2178,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -1900,7 +2215,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -1950,7 +2265,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -1967,6 +2282,11 @@ } } }, + "buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=" + }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -2015,7 +2335,7 @@ }, "cacache": { "version": "10.0.4", - "resolved": "http://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", "dev": true, "requires": { @@ -2092,7 +2412,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -2123,7 +2443,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -2590,7 +2910,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -2603,7 +2923,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -2721,7 +3041,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -3055,7 +3375,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -3119,7 +3439,7 @@ "dependencies": { "domelementtype": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", "dev": true }, @@ -3131,6 +3451,11 @@ } } }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" + }, "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", @@ -3307,7 +3632,7 @@ }, "entities": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", "dev": true }, @@ -3731,7 +4056,7 @@ }, "eventemitter2": { "version": "0.4.14", - "resolved": "http://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", "dev": true }, @@ -3743,7 +4068,7 @@ }, "events": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "dev": true }, @@ -4124,6 +4449,11 @@ "integrity": "sha1-peeo/7+kk7Q7kju9TKiaU7Y7YSs=", "dev": true }, + "file-type": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz", + "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==" + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -4149,7 +4479,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -4280,6 +4610,14 @@ } } }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -4377,7 +4715,7 @@ }, "fs-extra": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", "dev": true, "requires": { @@ -4445,12 +4783,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4470,7 +4810,8 @@ "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", @@ -4592,7 +4933,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -4618,6 +4960,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5023,7 +5366,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -5083,6 +5426,22 @@ } } }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "requires": { + "min-document": "^2.19.0", + "process": "~0.5.1" + }, + "dependencies": { + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + } + } + }, "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", @@ -5103,7 +5462,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -5173,7 +5532,7 @@ }, "grunt-cli": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", "dev": true, "requires": { @@ -5221,7 +5580,7 @@ "dependencies": { "shelljs": { "version": "0.5.3", - "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", "integrity": "sha1-xUmCuZbHbvDB5rWfvcWCX1txMRM=", "dev": true } @@ -5241,7 +5600,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -5269,7 +5628,7 @@ }, "grunt-contrib-jshint": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", "integrity": "sha1-Np2QmyWTxA6L55lAshNAhQx5Oaw=", "dev": true, "requires": { @@ -5368,7 +5727,7 @@ "dependencies": { "colors": { "version": "1.1.2", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true } @@ -5432,7 +5791,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -5450,7 +5809,7 @@ }, "handle-thing": { "version": "1.2.5", - "resolved": "http://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", "dev": true }, @@ -5677,7 +6036,7 @@ }, "html-webpack-plugin": { "version": "3.2.0", - "resolved": "http://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", "dev": true, "requires": { @@ -5725,7 +6084,7 @@ }, "htmlparser2": { "version": "3.8.3", - "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", "dev": true, "requires": { @@ -5744,7 +6103,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -5773,7 +6132,7 @@ }, "http-proxy-middleware": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { @@ -5893,8 +6252,7 @@ "ieee754": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", - "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", - "dev": true + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==" }, "iferr": { "version": "0.1.5", @@ -6225,7 +6583,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -6235,8 +6593,7 @@ "is-callable": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" }, "is-data-descriptor": { "version": "0.1.4", @@ -6316,6 +6673,11 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=" + }, "is-glob": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", @@ -6462,6 +6824,18 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, + "jimp": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.6.0.tgz", + "integrity": "sha512-RYpN+AAlTEMf8Bnkhq2eeTNyr70rDK/2UUfUqzBJmwmZwdR6fxRJvgbCGWT1BDVRxaAqo+4CWm8ePBxOIsr4jg==", + "requires": { + "@babel/polyfill": "^7.0.0", + "@jimp/custom": "^0.6.0", + "@jimp/plugins": "^0.6.0", + "@jimp/types": "^0.6.0", + "core-js": "^2.5.7" + } + }, "jison": { "version": "0.4.13", "resolved": "https://registry.npmjs.org/jison/-/jison-0.4.13.tgz", @@ -6508,6 +6882,11 @@ "nomnom": "1.5.2" } }, + "jpeg-js": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.4.tgz", + "integrity": "sha512-6IzjQxvnlT8UlklNmDXIJMWxijULjqGrzgqc0OG7YadZdvm7KPQ1j0ehmQQHckgEWOfgpptzcnWgESovxudpTA==" + }, "jquery": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", @@ -6750,7 +7129,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -6810,6 +7189,11 @@ "verror": "1.10.0" } }, + "jsqr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/jsqr/-/jsqr-1.1.1.tgz", + "integrity": "sha512-FVoMU2ncTyjaOqN/vwvDnZ7jaAVvFzM3LK3vG3jvQZFWJQlAwJ1XTCOgAEKo+4Rkd6ydMXTTvqGV/4w5VunmTw==" + }, "jsrsasign": { "version": "8.0.12", "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.12.tgz", @@ -6856,7 +7240,7 @@ }, "kew": { "version": "0.7.0", - "resolved": "http://registry.npmjs.org/kew/-/kew-0.7.0.tgz", + "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", "dev": true }, @@ -6934,6 +7318,28 @@ "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", "dev": true }, + "load-bmfont": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.0.tgz", + "integrity": "sha512-kT63aTAlNhZARowaNYcY29Fn/QYkc52M3l6V1ifRcPewg2lvUZDAj7R6dXjOL9D0sict76op3T5+odumDSF81g==", + "requires": { + "buffer-equal": "0.0.1", + "mime": "^1.3.4", + "parse-bmfont-ascii": "^1.0.3", + "parse-bmfont-binary": "^1.0.5", + "parse-bmfont-xml": "^1.1.4", + "phin": "^2.9.1", + "xhr": "^2.0.1", + "xtend": "^4.0.0" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + } + } + }, "load-grunt-tasks": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-3.5.2.tgz", @@ -6948,7 +7354,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -6961,7 +7367,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -7196,7 +7602,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -7255,7 +7661,7 @@ }, "meow": { "version": "3.7.0", - "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { @@ -7347,6 +7753,14 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -7432,9 +7846,8 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, "requires": { "minimist": "0.0.8" }, @@ -7442,8 +7855,7 @@ "minimist": { "version": "0.0.8", "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } }, @@ -7554,7 +7966,7 @@ }, "ncp": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=", "dev": true }, @@ -7617,7 +8029,7 @@ "dependencies": { "semver": { "version": "5.3.0", - "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", "dev": true } @@ -7756,7 +8168,7 @@ "dependencies": { "colors": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" }, "underscore": { @@ -7943,6 +8355,11 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, + "omggif": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.9.tgz", + "integrity": "sha1-3LcCTazVDFK00wPwSALJHAV8dl8=" + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -8015,13 +8432,13 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-locale": { "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -8030,7 +8447,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -8116,8 +8533,7 @@ "pako": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", - "dev": true + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" }, "parallel-transform": { "version": "1.1.0", @@ -8173,7 +8589,7 @@ }, "parse-asn1": { "version": "5.1.1", - "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", "dev": true, "requires": { @@ -8184,6 +8600,34 @@ "pbkdf2": "^3.0.3" } }, + "parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU=" + }, + "parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY=" + }, + "parse-bmfont-xml": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz", + "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==", + "requires": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.4.5" + } + }, + "parse-headers": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.1.tgz", + "integrity": "sha1-aug6eqJanZtwCswoaYzR8e1+lTY=", + "requires": { + "for-each": "^0.3.2", + "trim": "0.0.1" + } + }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -8231,7 +8675,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -8272,7 +8716,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -8341,6 +8785,11 @@ "which": "^1.2.10" } }, + "phin": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", + "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==" + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -8362,6 +8811,14 @@ "pinkie": "^2.0.0" } }, + "pixelmatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", + "integrity": "sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=", + "requires": { + "pngjs": "^3.0.0" + } + }, "pkg-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", @@ -8419,6 +8876,11 @@ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "dev": true }, + "pngjs": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.3.3.tgz", + "integrity": "sha512-1n3Z4p3IOxArEs1VRXnZ/RXdfEniAUS9jb68g58FIXMNkPJeZd+Qh4Uq7/e0LVxAQGos1eIUrqrt4FpjdnEd+Q==" + }, "popper.js": { "version": "1.14.5", "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.5.tgz", @@ -8437,7 +8899,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -8839,7 +9301,7 @@ }, "progress": { "version": "1.1.8", - "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=" }, "promise-inflight": { @@ -8864,13 +9326,13 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true }, "winston": { "version": "2.1.1", - "resolved": "http://registry.npmjs.org/winston/-/winston-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", "dev": true, "requires": { @@ -8885,7 +9347,7 @@ "dependencies": { "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true }, @@ -8973,6 +9435,11 @@ "resolved": "https://registry.npmjs.org/purepack/-/purepack-1.0.4.tgz", "integrity": "sha1-CGKC/ZOShfWGZLqam7oxzbFlzNI=" }, + "qr-image": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/qr-image/-/qr-image-3.2.0.tgz", + "integrity": "sha1-n6gpW+rlDEoUnPn5CaHbRkqGcug=" + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -9064,7 +9531,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -9253,7 +9720,7 @@ "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -9304,7 +9771,7 @@ }, "htmlparser2": { "version": "3.3.0", - "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", "dev": true, "requires": { @@ -9316,7 +9783,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -9426,7 +9893,7 @@ }, "require-uncached": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { @@ -9593,7 +10060,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -9741,8 +10208,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "schema-utils": { "version": "0.4.7", @@ -9914,7 +10380,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -9958,7 +10424,7 @@ }, "shelljs": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", "dev": true }, @@ -10610,7 +11076,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -10627,7 +11093,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -10706,7 +11172,7 @@ }, "tar": { "version": "2.2.1", - "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, "requires": { @@ -10734,7 +11200,7 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -10795,6 +11261,11 @@ "setimmediate": "^1.0.4" } }, + "timm": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/timm/-/timm-1.6.1.tgz", + "integrity": "sha512-hqDTYi/bWuDxL2i6T3v6nrvkAQ/1Bc060GSkVEQZp02zTSTB4CHSKsOkliequCftQaNRcjRqUZmpGWs5FfhrNg==" + }, "tiny-lr": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", @@ -10826,6 +11297,11 @@ } } }, + "tinycolor2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", + "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -10921,6 +11397,11 @@ "punycode": "^2.1.0" } }, + "trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" + }, "trim-newlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", @@ -11334,6 +11815,14 @@ "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" }, + "utif": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz", + "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==", + "requires": { + "pako": "^1.0.5" + } + }, "util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", @@ -11381,7 +11870,7 @@ "dependencies": { "async": { "version": "0.9.2", - "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -11407,7 +11896,7 @@ }, "valid-data-url": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", "integrity": "sha512-FXg2qXMzfAhZc0y2HzELNfUeiOjPr+52hU1DNBWiJJ2luXD+dD1R9NA48Ug5aj0ibbxroeGDc/RJv6ThiGgkDw==", "dev": true }, @@ -11423,7 +11912,7 @@ }, "validator": { "version": "9.4.1", - "resolved": "http://registry.npmjs.org/validator/-/validator-9.4.1.tgz", + "resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz", "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA==", "dev": true }, @@ -11847,7 +12336,7 @@ }, "webpack-node-externals": { "version": "1.7.2", - "resolved": "http://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz", "integrity": "sha512-ajerHZ+BJKeCLviLUUmnyd5B4RavLF76uv3cs6KNuO8W+HuQaEs0y0L7o40NQxdPy5w0pcv8Ew7yPUAQG0UdCg==", "dev": true }, @@ -11944,14 +12433,14 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true, "optional": true }, "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true, "optional": true @@ -11984,7 +12473,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -12038,12 +12527,44 @@ "async-limiter": "~1.0.0" } }, + "xhr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz", + "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==", + "requires": { + "global": "~4.3.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha1-qQKekp09vN7RafPG4oI42VpdWig=" + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + }, + "dependencies": { + "xmlbuilder": { + "version": "9.0.7", + "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + } + } + }, "xmlbuilder": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz", @@ -12074,8 +12595,7 @@ "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, "y18n": { "version": "3.2.1", diff --git a/src/core/operations/GenerateQRCode.mjs b/src/core/operations/GenerateQRCode.mjs index d634b1bd..edab6d40 100644 --- a/src/core/operations/GenerateQRCode.mjs +++ b/src/core/operations/GenerateQRCode.mjs @@ -9,6 +9,7 @@ import OperationError from "../errors/OperationError"; import qr from "qr-image"; import { toBase64 } from "../lib/Base64"; import Magic from "../lib/Magic"; +import Utils from "../Utils"; /** * Generate QR Code operation @@ -23,7 +24,7 @@ class GenerateQRCode extends Operation { this.name = "Generate QR Code"; this.module = "Image"; - this.description = "Generates a QR code from text."; + this.description = "Generates a Quick Response (QR) code from the input text.

A QR code is a type of matrix barcode (or two-dimensional barcode) first designed in 1994 for the automotive industry in Japan. A barcode is a machine-readable optical label that contains information about the item to which it is attached."; this.infoURL = "https://wikipedia.org/wiki/QR_code"; this.inputType = "string"; this.outputType = "byteArray"; @@ -32,17 +33,23 @@ class GenerateQRCode extends Operation { { "name": "Image Format", "type": "option", - "value": ["PNG", "SVG"] + "value": ["PNG", "SVG", "EPS", "PDF"] }, { - "name": "Size of QR module", + "name": "Module size (px)", "type": "number", "value": 5 }, { - "name": "Margin", + "name": "Margin (num modules)", "type": "number", "value": 2 + }, + { + "name": "Error correction", + "type": "option", + "value": ["Low", "Medium", "Quartile", "High"], + "defaultIndex": 1 } ]; } @@ -50,28 +57,39 @@ class GenerateQRCode extends Operation { /** * @param {string} input * @param {Object[]} args - * @returns {File} + * @returns {byteArray} */ run(input, args) { + const [format, size, margin, errorCorrection] = args; + // Create new QR image from the input data, and convert it to a buffer - const [format, size, margin] = args; - const qrImage = qr.imageSync(input, { type: format, size: size, margin: margin }); + const qrImage = qr.imageSync(input, { + type: format, + size: size, + margin: margin, + "ec_level": errorCorrection.charAt(0).toUpperCase() + }); + if (qrImage == null) { throw new OperationError("Error generating QR code."); } - if (format === "SVG") { - return [...Buffer.from(qrImage)]; - } else if (format === "PNG") { - // Return the QR image buffer as a byte array - return [...qrImage]; - } else { - throw new OperationError("Error generating QR code."); + + switch (format) { + case "SVG": + case "EPS": + case "PDF": + return [...Buffer.from(qrImage)]; + case "PNG": + // Return the QR image buffer as a byte array + return [...qrImage]; + default: + throw new OperationError("Unsupported QR code format."); } } /** * Displays the QR image using HTML for web apps - * + * * @param {byteArray} data * @returns {html} */ @@ -79,24 +97,21 @@ class GenerateQRCode extends Operation { if (!data.length) return ""; const [format] = args; - if (format === "SVG") { - let outputData = ""; - for (let i = 0; i < data.length; i++){ - outputData += String.fromCharCode(parseInt(data[i])); - } - return outputData; - } else { + + if (format === "PNG") { let dataURI = "data:"; const type = Magic.magicFileType(data); if (type && type.mime.indexOf("image") === 0){ dataURI += type.mime + ";"; } else { - throw new OperationError("Invalid file type"); + throw new OperationError("Invalid PNG file generated by QR image"); } dataURI += "base64," + toBase64(data); - return ""; + return ``; } + + return Utils.byteArrayToChars(data); } } diff --git a/src/core/operations/ParseQRCode.mjs b/src/core/operations/ParseQRCode.mjs index 34bd5d71..75a24d55 100644 --- a/src/core/operations/ParseQRCode.mjs +++ b/src/core/operations/ParseQRCode.mjs @@ -23,7 +23,7 @@ class ParseQRCode extends Operation { this.name = "Parse QR Code"; this.module = "Image"; - this.description = "Reads an image file and attempts to detect and read a QR code from the image.

Normalise Image
Attempt to normalise the image before parsing it, to try and improve detection of a QR code."; + this.description = "Reads an image file and attempts to detect and read a Quick Response (QR) code from the image.

Normalise Image
Attempts to normalise the image before parsing it to improve detection of a QR code."; this.infoURL = "https://wikipedia.org/wiki/QR_code"; this.inputType = "byteArray"; this.outputType = "string"; @@ -31,7 +31,7 @@ class ParseQRCode extends Operation { { "name": "Normalise image", "type": "boolean", - "value": true + "value": false } ]; } @@ -44,17 +44,19 @@ class ParseQRCode extends Operation { async run(input, args) { const type = Magic.magicFileType(input); const [normalise] = args; + // Make sure that the input is an image - if (type && type.mime.indexOf("image") === 0){ - let normalisedImage = null; - if (normalise){ + if (type && type.mime.indexOf("image") === 0) { + let image = input; + + if (normalise) { // Process the image to be easier to read by jsqr // Disables the alpha channel // Sets the image default background to white // Normalises the image colours // Makes the image greyscale - // Converts image to a JPEG - normalisedImage = await new Promise((resolve, reject) => { + // Converts image to a JPEG + image = await new Promise((resolve, reject) => { jimp.read(Buffer.from(input)) .then(image => { image @@ -63,38 +65,38 @@ class ParseQRCode extends Operation { .normalize() .greyscale() .getBuffer(jimp.MIME_JPEG, (error, result) => { - resolve([...result]); + resolve(result); }); }) .catch(err => { reject(new OperationError("Error reading the image file.")); }); }); - } else { - normalisedImage = input; } - if (normalisedImage instanceof OperationError){ - return normalisedImage; + + if (image instanceof OperationError) { + throw image; } + return new Promise((resolve, reject) => { - jimp.read(Buffer.from(normalisedImage)) + jimp.read(Buffer.from(image)) .then(image => { - if (image.bitmap != null){ + if (image.bitmap != null) { const qrData = jsqr(image.bitmap.data, image.getWidth(), image.getHeight()); - if (qrData != null){ + if (qrData != null) { resolve(qrData.data); } else { reject(new OperationError("Couldn't read a QR code from the image.")); } } else { - reject(new OperationError("Error reading the normalised image file.")); + reject(new OperationError("Error reading the image file.")); } }) .catch(err => { - reject(new OperationError("Error reading the normalised image file.")); + reject(new OperationError("Error reading the image file.")); }); }); - } else { + } else { throw new OperationError("Invalid file type."); } diff --git a/test/tests/operations/ParseQRCode.mjs b/test/tests/operations/ParseQRCode.mjs index d1b7990c..55c402ae 100644 --- a/test/tests/operations/ParseQRCode.mjs +++ b/test/tests/operations/ParseQRCode.mjs @@ -1,6 +1,6 @@ -/** +/** * Parse QR Code tests - * + * * @author j433866 [j433866@gmail.com] * @copyright Crown Copyright 2018 * @license Apache-2.0 @@ -15,7 +15,7 @@ TestRegister.addTests([ recipeConfig: [ { "op": "From Hex", - "args": ["Space"] + "args": ["None"] }, { "op": "Parse QR Code", @@ -34,7 +34,7 @@ TestRegister.addTests([ }, { "op": "Parse QR Code", - "args": [true] + "args": [false] }, ], }, @@ -45,7 +45,7 @@ TestRegister.addTests([ recipeConfig: [ { "op": "From Hex", - "args": ["Space"] + "args": ["None"] }, { "op": "Parse QR Code", @@ -60,7 +60,7 @@ TestRegister.addTests([ recipeConfig: [ { "op": "From Hex", - "args": ["Space"] + "args": ["None"] }, { "op": "Parse QR Code", From d2325306db9a82475a8ade5fa7fdcc6953f71c80 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 25 Dec 2018 21:55:51 +0000 Subject: [PATCH 029/147] 8.17.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2985f54f..b89e8ebd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.16.1", + "version": "8.17.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b0d0529e..fe93197d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.16.1", + "version": "8.17.0", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From b29bb6fdd7d780317b7ebe37ce1bd6f039401c00 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 25 Dec 2018 22:38:53 +0000 Subject: [PATCH 030/147] Added 'DishError' and refined test results. --- src/core/Dish.mjs | 149 ++++++++++++++------------ src/core/Recipe.mjs | 6 +- src/core/errors/DishError.mjs | 26 +++++ src/core/operations/ToMessagePack.mjs | 2 +- src/core/vendor/remove-exif.mjs | 2 +- test/index.mjs | 5 +- test/tests/operations/Code.mjs | 4 +- 7 files changed, 117 insertions(+), 77 deletions(-) create mode 100644 src/core/errors/DishError.mjs diff --git a/src/core/Dish.mjs b/src/core/Dish.mjs index af0fa32d..2276a36c 100755 --- a/src/core/Dish.mjs +++ b/src/core/Dish.mjs @@ -6,6 +6,7 @@ */ import Utils from "./Utils"; +import DishError from "./errors/DishError"; import BigNumber from "bignumber.js"; import log from "loglevel"; @@ -61,7 +62,7 @@ class Dish { case "list": return Dish.LIST_FILE; default: - throw "Invalid data type string. No matching enum."; + throw new DishError("Invalid data type string. No matching enum."); } } @@ -93,7 +94,7 @@ class Dish { case Dish.LIST_FILE: return "List"; default: - throw "Invalid data type enum. No matching type."; + throw new DishError("Invalid data type enum. No matching type."); } } @@ -117,7 +118,7 @@ class Dish { if (!this.valid()) { const sample = Utils.truncate(JSON.stringify(this.value), 13); - throw "Data is not a valid " + Dish.enumLookup(type) + ": " + sample; + throw new DishError(`Data is not a valid ${Dish.enumLookup(type)}: ${sample}`); } } @@ -151,77 +152,85 @@ class Dish { const byteArrayToStr = notUTF8 ? Utils.byteArrayToChars : Utils.byteArrayToUtf8; // Convert data to intermediate byteArray type - switch (this.type) { - case Dish.STRING: - this.value = this.value ? Utils.strToByteArray(this.value) : []; - break; - case Dish.NUMBER: - this.value = typeof this.value === "number" ? Utils.strToByteArray(this.value.toString()) : []; - break; - case Dish.HTML: - this.value = this.value ? Utils.strToByteArray(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : []; - break; - case Dish.ARRAY_BUFFER: - // Array.from() would be nicer here, but it's slightly slower - this.value = Array.prototype.slice.call(new Uint8Array(this.value)); - break; - case Dish.BIG_NUMBER: - this.value = this.value instanceof BigNumber ? Utils.strToByteArray(this.value.toFixed()) : []; - break; - case Dish.JSON: - this.value = this.value ? Utils.strToByteArray(JSON.stringify(this.value, null, 4)) : []; - break; - case Dish.FILE: - this.value = await Utils.readFile(this.value); - this.value = Array.prototype.slice.call(this.value); - break; - case Dish.LIST_FILE: - this.value = await Promise.all(this.value.map(async f => Utils.readFile(f))); - this.value = this.value.map(b => Array.prototype.slice.call(b)); - this.value = [].concat.apply([], this.value); - break; - default: - break; + try { + switch (this.type) { + case Dish.STRING: + this.value = this.value ? Utils.strToByteArray(this.value) : []; + break; + case Dish.NUMBER: + this.value = typeof this.value === "number" ? Utils.strToByteArray(this.value.toString()) : []; + break; + case Dish.HTML: + this.value = this.value ? Utils.strToByteArray(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : []; + break; + case Dish.ARRAY_BUFFER: + // Array.from() would be nicer here, but it's slightly slower + this.value = Array.prototype.slice.call(new Uint8Array(this.value)); + break; + case Dish.BIG_NUMBER: + this.value = this.value instanceof BigNumber ? Utils.strToByteArray(this.value.toFixed()) : []; + break; + case Dish.JSON: + this.value = this.value ? Utils.strToByteArray(JSON.stringify(this.value, null, 4)) : []; + break; + case Dish.FILE: + this.value = await Utils.readFile(this.value); + this.value = Array.prototype.slice.call(this.value); + break; + case Dish.LIST_FILE: + this.value = await Promise.all(this.value.map(async f => Utils.readFile(f))); + this.value = this.value.map(b => Array.prototype.slice.call(b)); + this.value = [].concat.apply([], this.value); + break; + default: + break; + } + } catch (err) { + throw new DishError(`Error translating from ${Dish.enumLookup(this.type)} to byteArray: ${err}`); } this.type = Dish.BYTE_ARRAY; // Convert from byteArray to toType - switch (toType) { - case Dish.STRING: - case Dish.HTML: - this.value = this.value ? byteArrayToStr(this.value) : ""; - this.type = Dish.STRING; - break; - case Dish.NUMBER: - this.value = this.value ? parseFloat(byteArrayToStr(this.value)) : 0; - this.type = Dish.NUMBER; - break; - case Dish.ARRAY_BUFFER: - this.value = new Uint8Array(this.value).buffer; - this.type = Dish.ARRAY_BUFFER; - break; - case Dish.BIG_NUMBER: - try { - this.value = new BigNumber(byteArrayToStr(this.value)); - } catch (err) { - this.value = new BigNumber(NaN); - } - this.type = Dish.BIG_NUMBER; - break; - case Dish.JSON: - this.value = JSON.parse(byteArrayToStr(this.value)); - this.type = Dish.JSON; - break; - case Dish.FILE: - this.value = new File(this.value, "unknown"); - break; - case Dish.LIST_FILE: - this.value = [new File(this.value, "unknown")]; - this.type = Dish.LIST_FILE; - break; - default: - break; + try { + switch (toType) { + case Dish.STRING: + case Dish.HTML: + this.value = this.value ? byteArrayToStr(this.value) : ""; + this.type = Dish.STRING; + break; + case Dish.NUMBER: + this.value = this.value ? parseFloat(byteArrayToStr(this.value)) : 0; + this.type = Dish.NUMBER; + break; + case Dish.ARRAY_BUFFER: + this.value = new Uint8Array(this.value).buffer; + this.type = Dish.ARRAY_BUFFER; + break; + case Dish.BIG_NUMBER: + try { + this.value = new BigNumber(byteArrayToStr(this.value)); + } catch (err) { + this.value = new BigNumber(NaN); + } + this.type = Dish.BIG_NUMBER; + break; + case Dish.JSON: + this.value = JSON.parse(byteArrayToStr(this.value)); + this.type = Dish.JSON; + break; + case Dish.FILE: + this.value = new File(this.value, "unknown"); + 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 byteArray to ${Dish.enumLookup(toType)}: ${err}`); } } @@ -357,7 +366,7 @@ class Dish { ); break; default: - throw new Error("Cannot clone Dish, unknown type"); + throw new DishError("Cannot clone Dish, unknown type"); } return newDish; diff --git a/src/core/Recipe.mjs b/src/core/Recipe.mjs index a5b2e81f..b21f76b8 100755 --- a/src/core/Recipe.mjs +++ b/src/core/Recipe.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -// import Operation from "./Operation.js"; import OpModules from "./config/modules/OpModules"; import OperationConfig from "./config/OperationConfig.json"; import OperationError from "./errors/OperationError"; +import DishError from "./errors/DishError"; import log from "loglevel"; /** @@ -183,6 +183,10 @@ class Recipe { // native types is not fully supported yet. dish.set(err.message, "string"); return i; + } else if (err instanceof DishError || + (err.type && err.type === "DishError")) { + dish.set(err.message, "string"); + return i; } else { const e = typeof err == "string" ? { message: err } : err; diff --git a/src/core/errors/DishError.mjs b/src/core/errors/DishError.mjs new file mode 100644 index 00000000..922ebd4c --- /dev/null +++ b/src/core/errors/DishError.mjs @@ -0,0 +1,26 @@ +/** + * Custom error type for handling Dish type errors. + * i.e. where the Dish cannot be successfully translated between types + * + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +class DishError extends Error { + /** + * Standard error constructor. Adds no new behaviour. + * + * @param args - Standard error args + */ + constructor(...args) { + super(...args); + + this.type = "DishError"; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, DishError); + } + } +} + +export default DishError; diff --git a/src/core/operations/ToMessagePack.mjs b/src/core/operations/ToMessagePack.mjs index 40e31a41..ffcf3b43 100644 --- a/src/core/operations/ToMessagePack.mjs +++ b/src/core/operations/ToMessagePack.mjs @@ -5,7 +5,7 @@ */ import Operation from "../Operation"; -import OperationError from "../errors/OperationError.mjs"; +import OperationError from "../errors/OperationError"; import notepack from "notepack.io"; /** diff --git a/src/core/vendor/remove-exif.mjs b/src/core/vendor/remove-exif.mjs index 6c5c9e53..6377c674 100644 --- a/src/core/vendor/remove-exif.mjs +++ b/src/core/vendor/remove-exif.mjs @@ -18,7 +18,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import Utils from "../Utils.mjs"; +import Utils from "../Utils"; // Param jpeg should be a binaryArray export function removeEXIF(jpeg) { diff --git a/test/index.mjs b/test/index.mjs index 4a85a8a1..ff13fe3b 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -77,7 +77,7 @@ import "./tests/operations/SetUnion"; import "./tests/operations/StrUtils"; import "./tests/operations/SymmetricDifference"; import "./tests/operations/TextEncodingBruteForce"; -import "./tests/operations/ToGeohash.mjs"; +import "./tests/operations/ToGeohash"; import "./tests/operations/TranslateDateTimeFormat"; import "./tests/operations/Magic"; import "./tests/operations/ParseTLV"; @@ -155,7 +155,8 @@ TestRegister.runTests() } if (!allTestsPassing) { - console.log("\nNot all tests are passing"); + console.log("\nFailing tests:\n"); + results.filter(r => r.status !== "passing").forEach(handleTestResult); } process.exit(allTestsPassing ? 0 : 1); diff --git a/test/tests/operations/Code.mjs b/test/tests/operations/Code.mjs index 6d7cd102..4c716546 100644 --- a/test/tests/operations/Code.mjs +++ b/test/tests/operations/Code.mjs @@ -334,8 +334,8 @@ TestRegister.addTests([ }, { name: "To MessagePack: no content", - input: "", - expectedError: true, + input: "{}", + expectedMatch: /Unexpected end of JSON input/, recipeConfig: [ { "op": "To MessagePack", From 43dcd544f2ced678e27d4214e1910ef1c3c02df9 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 25 Dec 2018 23:58:00 +0000 Subject: [PATCH 031/147] Added webpack-bundle-analyzer to production build. Refactored some modules to improve performance. Removed moment-timezone from Utils to drastically reduce module size. --- Gruntfile.js | 6 + package-lock.json | 170 ++++++++++++++++-- package.json | 2 +- src/core/Utils.mjs | 33 ---- src/core/operations/Adler32Checksum.mjs | 2 +- src/core/operations/AnalyseHash.mjs | 2 +- src/core/operations/Bcrypt.mjs | 2 +- src/core/operations/BcryptCompare.mjs | 2 +- src/core/operations/BcryptParse.mjs | 2 +- src/core/operations/CRC16Checksum.mjs | 2 +- src/core/operations/CRC32Checksum.mjs | 2 +- src/core/operations/CTPH.mjs | 2 +- src/core/operations/CompareCTPHHashes.mjs | 2 +- src/core/operations/CompareSSDEEPHashes.mjs | 2 +- src/core/operations/DecodeText.mjs | 2 +- src/core/operations/EncodeText.mjs | 2 +- src/core/operations/Fletcher16Checksum.mjs | 2 +- src/core/operations/Fletcher32Checksum.mjs | 2 +- src/core/operations/Fletcher64Checksum.mjs | 2 +- src/core/operations/Fletcher8Checksum.mjs | 2 +- src/core/operations/FromGeohash.mjs | 2 +- src/core/operations/GenerateAllHashes.mjs | 2 +- src/core/operations/GroupIPAddresses.mjs | 2 +- src/core/operations/HAS160.mjs | 2 +- src/core/operations/HMAC.mjs | 2 +- src/core/operations/Keccak.mjs | 2 +- src/core/operations/MD2.mjs | 2 +- src/core/operations/MD4.mjs | 2 +- src/core/operations/MD5.mjs | 2 +- src/core/operations/MD6.mjs | 2 +- src/core/operations/ParseIPRange.mjs | 2 +- src/core/operations/ParseIPv4Header.mjs | 2 +- src/core/operations/ParseIPv6Address.mjs | 6 +- src/core/operations/RIPEMD.mjs | 2 +- src/core/operations/SHA0.mjs | 2 +- src/core/operations/SHA1.mjs | 2 +- src/core/operations/SHA2.mjs | 2 +- src/core/operations/SHA3.mjs | 2 +- src/core/operations/SSDEEP.mjs | 2 +- src/core/operations/Scrypt.mjs | 2 +- src/core/operations/Shake.mjs | 2 +- src/core/operations/Snefru.mjs | 2 +- src/core/operations/TCPIPChecksum.mjs | 2 +- .../operations/TextEncodingBruteForce.mjs | 2 +- src/core/operations/ToGeohash.mjs | 2 +- src/core/operations/Whirlpool.mjs | 2 +- src/web/App.mjs | 4 +- test/tests/operations/Code.mjs | 6 +- 48 files changed, 207 insertions(+), 102 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index aee50b75..233a2888 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -2,6 +2,7 @@ const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); +const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin; const NodeExternals = require("webpack-node-externals"); const Inliner = require("web-resource-inliner"); const glob = require("glob"); @@ -209,6 +210,11 @@ module.exports = function (grunt) { minifyCSS: true } }), + new BundleAnalyzerPlugin({ + analyzerMode: "static", + reportFilename: "build/prod/BundleAnalyzerReport.html", + openAnalyzer: false + }), ] }, webInline: { diff --git a/package-lock.json b/package-lock.json index b89e8ebd..ecc63923 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1979,11 +1979,17 @@ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" }, - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true + "bfj": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.1.tgz", + "integrity": "sha512-+GUNvzHR4nRyGybQc2WpNJL4MJazMuvf92ueIyA0bIkPRwhhQu3IfZQ2PSoVPpCBJfmoSdOxu5rnotfFLlvYRQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "check-types": "^7.3.0", + "hoopy": "^0.1.2", + "tryer": "^1.0.0" + } }, "bignumber.js": { "version": "8.0.1", @@ -2459,6 +2465,12 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "check-types": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-7.4.0.tgz", + "integrity": "sha512-YbulWHdfP99UfZ73NcUDlNJhEIDgm9Doq9GhpyXbF+7Aegi3CVV7qqMCKTTqJxlvEvnQBp9IA+dxsGN6xK/nSg==", + "dev": true + }, "chi-squared": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/chi-squared/-/chi-squared-1.1.0.tgz", @@ -3496,6 +3508,12 @@ "domelementtype": "1" } }, + "duplexer": { + "version": "0.1.1", + "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, "duplexify": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", @@ -3577,6 +3595,12 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, + "ejs": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", + "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", + "dev": true + }, "electron-to-chromium": { "version": "1.3.84", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.84.tgz", @@ -4454,6 +4478,12 @@ "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz", "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==" }, + "filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -4783,14 +4813,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4810,8 +4838,7 @@ "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", @@ -4933,8 +4960,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -4960,7 +4986,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5807,6 +5832,16 @@ "lodash": "^4.7.0" } }, + "gzip-size": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.0.0.tgz", + "integrity": "sha512-5iI7omclyqrnWw4XbXAmGhPsABkSIDQonv2K0h61lybgofWa6iZyvrI3r2zsJH4P8Nb64fFVzlvfhs0g7BBxAA==", + "dev": true, + "requires": { + "duplexer": "^0.1.1", + "pify": "^3.0.0" + } + }, "handle-thing": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", @@ -5954,6 +5989,12 @@ "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", "dev": true }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "dev": true + }, "hosted-git-info": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", @@ -6065,6 +6106,14 @@ "emojis-list": "^2.0.0", "json5": "^0.5.0", "object-assign": "^4.0.1" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + } } } } @@ -6938,11 +6987,6 @@ "xmlcreate": "^1.0.1" } }, - "jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha1-sBMHyym2GKHtJux56RH4A8TaAEA=" - }, "jsdoc": { "version": "3.5.5", "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz", @@ -7390,6 +7434,12 @@ "json5": "^0.5.0" }, "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, "json5": { "version": "0.5.1", "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz", @@ -8393,6 +8443,12 @@ "mimic-fn": "^1.0.0" } }, + "opener": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", + "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", + "dev": true + }, "opn": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", @@ -11436,6 +11492,12 @@ "glob": "^7.1.2" } }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", + "dev": true + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", @@ -12086,6 +12148,78 @@ } } }, + "webpack-bundle-analyzer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.0.3.tgz", + "integrity": "sha512-naLWiRfmtH4UJgtUktRTLw6FdoZJ2RvCR9ePbwM9aRMsS/KjFerkPZG9epEvXRAw5d5oPdrs9+3p+afNjxW8Xw==", + "dev": true, + "requires": { + "acorn": "^5.7.3", + "bfj": "^6.1.1", + "chalk": "^2.4.1", + "commander": "^2.18.0", + "ejs": "^2.6.1", + "express": "^4.16.3", + "filesize": "^3.6.1", + "gzip-size": "^5.0.0", + "lodash": "^4.17.10", + "mkdirp": "^0.5.1", + "opener": "^1.5.1", + "ws": "^6.0.0" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "ws": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.2.tgz", + "integrity": "sha512-rfUqzvz0WxmSXtJpPMX2EeASXabOrSMk1ruMOV3JBTBjo4ac2lDjGGsbQSyxj8Odhw5fBib8ZKEjDNvgouNKYw==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, "webpack-dev-middleware": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz", diff --git a/package.json b/package.json index fe93197d..7bbfb7f1 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "url-loader": "^1.1.2", "web-resource-inliner": "^4.2.1", "webpack": "^4.25.1", + "webpack-bundle-analyzer": "^3.0.3", "webpack-dev-server": "^3.1.10", "webpack-node-externals": "^1.7.2", "worker-loader": "^2.0.0" @@ -96,7 +97,6 @@ "jquery": "^3.3.1", "js-crc": "^0.2.0", "js-sha3": "^0.8.0", - "jsbn": "^1.1.0", "jsesc": "^2.5.1", "jsonpath": "^1.0.0", "jsonwebtoken": "^8.3.0", diff --git a/src/core/Utils.mjs b/src/core/Utils.mjs index 342d6d6c..24f2d661 100755 --- a/src/core/Utils.mjs +++ b/src/core/Utils.mjs @@ -5,7 +5,6 @@ */ import utf8 from "utf8"; -import moment from "moment-timezone"; import {fromBase64} from "./lib/Base64"; import {fromHex} from "./lib/Hex"; import {fromDecimal} from "./lib/Decimal"; @@ -797,38 +796,6 @@ class Utils { } - /** - * Expresses a number of milliseconds in a human readable format. - * - * Range | Sample Output - * -----------------------------|------------------------------- - * 0 to 45 seconds | a few seconds ago - * 45 to 90 seconds | a minute ago - * 90 seconds to 45 minutes | 2 minutes ago ... 45 minutes ago - * 45 to 90 minutes | an hour ago - * 90 minutes to 22 hours | 2 hours ago ... 22 hours ago - * 22 to 36 hours | a day ago - * 36 hours to 25 days | 2 days ago ... 25 days ago - * 25 to 45 days | a month ago - * 45 to 345 days | 2 months ago ... 11 months ago - * 345 to 545 days (1.5 years) | a year ago - * 546 days+ | 2 years ago ... 20 years ago - * - * @param {number} ms - * @returns {string} - * - * @example - * // returns "3 minutes" - * Utils.fuzzyTime(152435); - * - * // returns "5 days" - * Utils.fuzzyTime(456851321); - */ - static fuzzyTime(ms) { - return moment.duration(ms, "milliseconds").humanize(); - } - - /** * Formats a list of files or directories. * diff --git a/src/core/operations/Adler32Checksum.mjs b/src/core/operations/Adler32Checksum.mjs index 41584de6..ad4b4072 100644 --- a/src/core/operations/Adler32Checksum.mjs +++ b/src/core/operations/Adler32Checksum.mjs @@ -19,7 +19,7 @@ class Adler32Checksum extends Operation { super(); this.name = "Adler-32 Checksum"; - this.module = "Hashing"; + 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).

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"; diff --git a/src/core/operations/AnalyseHash.mjs b/src/core/operations/AnalyseHash.mjs index cb719f77..17f78f30 100644 --- a/src/core/operations/AnalyseHash.mjs +++ b/src/core/operations/AnalyseHash.mjs @@ -19,7 +19,7 @@ class AnalyseHash extends Operation { super(); this.name = "Analyse hash"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Tries to determine information about a given hash and suggests which algorithm may have been used to generate it based on its length."; this.infoURL = "https://wikipedia.org/wiki/Comparison_of_cryptographic_hash_functions"; this.inputType = "string"; diff --git a/src/core/operations/Bcrypt.mjs b/src/core/operations/Bcrypt.mjs index fafd4e26..36a20607 100644 --- a/src/core/operations/Bcrypt.mjs +++ b/src/core/operations/Bcrypt.mjs @@ -19,7 +19,7 @@ class Bcrypt extends Operation { super(); this.name = "Bcrypt"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "bcrypt is a password hashing function designed by Niels Provos and David Mazi\xe8res, based on the Blowfish cipher, and presented at USENIX in 1999. Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over time, the iteration count (rounds) can be increased to make it slower, so it remains resistant to brute-force search attacks even with increasing computation power.

Enter the password in the input to generate its hash."; this.infoURL = "https://wikipedia.org/wiki/Bcrypt"; this.inputType = "string"; diff --git a/src/core/operations/BcryptCompare.mjs b/src/core/operations/BcryptCompare.mjs index f3edb2d9..5d8c393e 100644 --- a/src/core/operations/BcryptCompare.mjs +++ b/src/core/operations/BcryptCompare.mjs @@ -19,7 +19,7 @@ class BcryptCompare extends Operation { super(); this.name = "Bcrypt compare"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Tests whether the input matches the given bcrypt hash. To test multiple possible passwords, use the 'Fork' operation."; this.infoURL = "https://wikipedia.org/wiki/Bcrypt"; this.inputType = "string"; diff --git a/src/core/operations/BcryptParse.mjs b/src/core/operations/BcryptParse.mjs index fdd907b9..629eb1c2 100644 --- a/src/core/operations/BcryptParse.mjs +++ b/src/core/operations/BcryptParse.mjs @@ -20,7 +20,7 @@ class BcryptParse extends Operation { super(); this.name = "Bcrypt parse"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Parses a bcrypt hash to determine the number of rounds used, the salt, and the password hash."; this.infoURL = "https://wikipedia.org/wiki/Bcrypt"; this.inputType = "string"; diff --git a/src/core/operations/CRC16Checksum.mjs b/src/core/operations/CRC16Checksum.mjs index f0692f24..3171ad73 100644 --- a/src/core/operations/CRC16Checksum.mjs +++ b/src/core/operations/CRC16Checksum.mjs @@ -19,7 +19,7 @@ class CRC16Checksum extends Operation { super(); this.name = "CRC-16 Checksum"; - this.module = "Hashing"; + 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.

The CRC was invented by W. Wesley Peterson in 1961."; this.infoURL = "https://wikipedia.org/wiki/Cyclic_redundancy_check"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/CRC32Checksum.mjs b/src/core/operations/CRC32Checksum.mjs index b4e85799..962253dc 100644 --- a/src/core/operations/CRC32Checksum.mjs +++ b/src/core/operations/CRC32Checksum.mjs @@ -19,7 +19,7 @@ class CRC32Checksum extends Operation { super(); this.name = "CRC-32 Checksum"; - this.module = "Hashing"; + 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.

The CRC was invented by W. Wesley Peterson in 1961; the 32-bit CRC function of Ethernet and many other standards is the work of several researchers and was published in 1975."; this.infoURL = "https://wikipedia.org/wiki/Cyclic_redundancy_check"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/CTPH.mjs b/src/core/operations/CTPH.mjs index 3597cd55..feb58d44 100644 --- a/src/core/operations/CTPH.mjs +++ b/src/core/operations/CTPH.mjs @@ -19,7 +19,7 @@ class CTPH extends Operation { super(); this.name = "CTPH"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Context Triggered Piecewise Hashing, also called Fuzzy Hashing, can match inputs that have homologies. Such inputs have sequences of identical bytes in the same order, although bytes in between these sequences may be different in both content and length.

CTPH was originally based on the work of Dr. Andrew Tridgell and a spam email detector called SpamSum. This method was adapted by Jesse Kornblum and published at the DFRWS conference in 2006 in a paper 'Identifying Almost Identical Files Using Context Triggered Piecewise Hashing'."; this.infoURL = "https://forensicswiki.org/wiki/Context_Triggered_Piecewise_Hashing"; this.inputType = "string"; diff --git a/src/core/operations/CompareCTPHHashes.mjs b/src/core/operations/CompareCTPHHashes.mjs index acae6b42..b8cd4c55 100644 --- a/src/core/operations/CompareCTPHHashes.mjs +++ b/src/core/operations/CompareCTPHHashes.mjs @@ -22,7 +22,7 @@ class CompareCTPHHashes extends Operation { super(); this.name = "Compare CTPH hashes"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Compares two Context Triggered Piecewise Hashing (CTPH) fuzzy hashes to determine the similarity between them on a scale of 0 to 100."; this.infoURL = "https://forensicswiki.org/wiki/Context_Triggered_Piecewise_Hashing"; this.inputType = "string"; diff --git a/src/core/operations/CompareSSDEEPHashes.mjs b/src/core/operations/CompareSSDEEPHashes.mjs index fdd38210..46aa8dac 100644 --- a/src/core/operations/CompareSSDEEPHashes.mjs +++ b/src/core/operations/CompareSSDEEPHashes.mjs @@ -22,7 +22,7 @@ class CompareSSDEEPHashes extends Operation { super(); this.name = "Compare SSDEEP hashes"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Compares two SSDEEP fuzzy hashes to determine the similarity between them on a scale of 0 to 100."; this.infoURL = "https://forensicswiki.org/wiki/Ssdeep"; this.inputType = "string"; diff --git a/src/core/operations/DecodeText.mjs b/src/core/operations/DecodeText.mjs index e7a4f188..a5d60706 100644 --- a/src/core/operations/DecodeText.mjs +++ b/src/core/operations/DecodeText.mjs @@ -20,7 +20,7 @@ class DecodeText extends Operation { super(); this.name = "Decode text"; - this.module = "CharEnc"; + this.module = "Encodings"; this.description = [ "Decodes text from the chosen character encoding.", "

", diff --git a/src/core/operations/EncodeText.mjs b/src/core/operations/EncodeText.mjs index f634c3d9..dd3241a2 100644 --- a/src/core/operations/EncodeText.mjs +++ b/src/core/operations/EncodeText.mjs @@ -20,7 +20,7 @@ class EncodeText extends Operation { super(); this.name = "Encode text"; - this.module = "CharEnc"; + this.module = "Encodings"; this.description = [ "Encodes text into the chosen character encoding.", "

", diff --git a/src/core/operations/Fletcher16Checksum.mjs b/src/core/operations/Fletcher16Checksum.mjs index ff760282..d2331823 100644 --- a/src/core/operations/Fletcher16Checksum.mjs +++ b/src/core/operations/Fletcher16Checksum.mjs @@ -19,7 +19,7 @@ class Fletcher16Checksum extends Operation { super(); this.name = "Fletcher-16 Checksum"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.

The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques."; this.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum#Fletcher-16"; this.inputType = "byteArray"; diff --git a/src/core/operations/Fletcher32Checksum.mjs b/src/core/operations/Fletcher32Checksum.mjs index 5a3ad55b..30f4bc69 100644 --- a/src/core/operations/Fletcher32Checksum.mjs +++ b/src/core/operations/Fletcher32Checksum.mjs @@ -19,7 +19,7 @@ class Fletcher32Checksum extends Operation { super(); this.name = "Fletcher-32 Checksum"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.

The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques."; this.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum#Fletcher-32"; this.inputType = "byteArray"; diff --git a/src/core/operations/Fletcher64Checksum.mjs b/src/core/operations/Fletcher64Checksum.mjs index 2f60b4f3..56afe42e 100644 --- a/src/core/operations/Fletcher64Checksum.mjs +++ b/src/core/operations/Fletcher64Checksum.mjs @@ -19,7 +19,7 @@ class Fletcher64Checksum extends Operation { super(); this.name = "Fletcher-64 Checksum"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.

The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques."; this.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum#Fletcher-64"; this.inputType = "byteArray"; diff --git a/src/core/operations/Fletcher8Checksum.mjs b/src/core/operations/Fletcher8Checksum.mjs index 6cc538e2..a6dc0e4d 100644 --- a/src/core/operations/Fletcher8Checksum.mjs +++ b/src/core/operations/Fletcher8Checksum.mjs @@ -19,7 +19,7 @@ class Fletcher8Checksum extends Operation { super(); this.name = "Fletcher-8 Checksum"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.

The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques."; this.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum"; this.inputType = "byteArray"; diff --git a/src/core/operations/FromGeohash.mjs b/src/core/operations/FromGeohash.mjs index b70273da..da261555 100644 --- a/src/core/operations/FromGeohash.mjs +++ b/src/core/operations/FromGeohash.mjs @@ -19,7 +19,7 @@ class FromGeohash extends Operation { super(); this.name = "From Geohash"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Converts Geohash strings into Lat/Long coordinates. For example, ww8p1r4t8 becomes 37.8324,112.5584."; this.infoURL = "https://wikipedia.org/wiki/Geohash"; this.inputType = "string"; diff --git a/src/core/operations/GenerateAllHashes.mjs b/src/core/operations/GenerateAllHashes.mjs index 65820673..633337f0 100644 --- a/src/core/operations/GenerateAllHashes.mjs +++ b/src/core/operations/GenerateAllHashes.mjs @@ -41,7 +41,7 @@ class GenerateAllHashes extends Operation { super(); this.name = "Generate all hashes"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Generates all available hashes and checksums for the input."; this.infoURL = "https://wikipedia.org/wiki/Comparison_of_cryptographic_hash_functions"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/GroupIPAddresses.mjs b/src/core/operations/GroupIPAddresses.mjs index 1016852b..5e5d97c8 100644 --- a/src/core/operations/GroupIPAddresses.mjs +++ b/src/core/operations/GroupIPAddresses.mjs @@ -22,7 +22,7 @@ class GroupIPAddresses extends Operation { super(); this.name = "Group IP addresses"; - this.module = "JSBN"; + this.module = "Default"; this.description = "Groups a list of IP addresses into subnets. Supports both IPv4 and IPv6 addresses."; this.infoURL = "https://wikipedia.org/wiki/Subnetwork"; this.inputType = "string"; diff --git a/src/core/operations/HAS160.mjs b/src/core/operations/HAS160.mjs index 64259999..ab3db6a2 100644 --- a/src/core/operations/HAS160.mjs +++ b/src/core/operations/HAS160.mjs @@ -19,7 +19,7 @@ class HAS160 extends Operation { super(); this.name = "HAS-160"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "HAS-160 is a cryptographic hash function designed for use with the Korean KCDSA digital signature algorithm. It is derived from SHA-1, with assorted changes intended to increase its security. It produces a 160-bit output.

HAS-160 is used in the same way as SHA-1. First it divides input in blocks of 512 bits each and pads the final block. A digest function updates the intermediate hash value by processing the input blocks in turn.

The message digest algorithm consists of 80 rounds."; this.infoURL = "https://wikipedia.org/wiki/HAS-160"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/HMAC.mjs b/src/core/operations/HMAC.mjs index 6517c581..d511febb 100644 --- a/src/core/operations/HMAC.mjs +++ b/src/core/operations/HMAC.mjs @@ -20,7 +20,7 @@ class HMAC extends Operation { super(); this.name = "HMAC"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Keyed-Hash Message Authentication Codes (HMAC) are a mechanism for message authentication using cryptographic hash functions."; this.infoURL = "https://wikipedia.org/wiki/HMAC"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/Keccak.mjs b/src/core/operations/Keccak.mjs index 2044ee7f..0c930589 100644 --- a/src/core/operations/Keccak.mjs +++ b/src/core/operations/Keccak.mjs @@ -20,7 +20,7 @@ class Keccak extends Operation { super(); this.name = "Keccak"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "The Keccak hash algorithm was designed by Guido Bertoni, Joan Daemen, Micha\xebl Peeters, and Gilles Van Assche, building upon RadioGat\xfan. It was selected as the winner of the SHA-3 design competition.

This version of the algorithm is Keccak[c=2d] and differs from the SHA-3 specification."; this.infoURL = "https://wikipedia.org/wiki/SHA-3"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/MD2.mjs b/src/core/operations/MD2.mjs index bc69ecfb..dfe2c7a3 100644 --- a/src/core/operations/MD2.mjs +++ b/src/core/operations/MD2.mjs @@ -19,7 +19,7 @@ class MD2 extends Operation { super(); this.name = "MD2"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "The MD2 (Message-Digest 2) algorithm is a cryptographic hash function developed by Ronald Rivest in 1989. The algorithm is optimized for 8-bit computers.

Although MD2 is no longer considered secure, even as of 2014, it remains in use in public key infrastructures as part of certificates generated with MD2 and RSA."; this.infoURL = "https://wikipedia.org/wiki/MD2_(cryptography)"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/MD4.mjs b/src/core/operations/MD4.mjs index 129769af..7872c7b8 100644 --- a/src/core/operations/MD4.mjs +++ b/src/core/operations/MD4.mjs @@ -19,7 +19,7 @@ class MD4 extends Operation { super(); this.name = "MD4"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "The MD4 (Message-Digest 4) algorithm is a cryptographic hash function developed by Ronald Rivest in 1990. The digest length is 128 bits. The algorithm has influenced later designs, such as the MD5, SHA-1 and RIPEMD algorithms.

The security of MD4 has been severely compromised."; this.infoURL = "https://wikipedia.org/wiki/MD4"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/MD5.mjs b/src/core/operations/MD5.mjs index 6329b641..96de7108 100644 --- a/src/core/operations/MD5.mjs +++ b/src/core/operations/MD5.mjs @@ -19,7 +19,7 @@ class MD5 extends Operation { super(); this.name = "MD5"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "MD5 (Message-Digest 5) is a widely used hash function. It has been used in a variety of security applications and is also commonly used to check the integrity of files.

However, MD5 is not collision resistant and it isn't suitable for applications like SSL/TLS certificates or digital signatures that rely on this property."; this.infoURL = "https://wikipedia.org/wiki/MD5"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/MD6.mjs b/src/core/operations/MD6.mjs index e29db447..0e53a1b0 100644 --- a/src/core/operations/MD6.mjs +++ b/src/core/operations/MD6.mjs @@ -20,7 +20,7 @@ class MD6 extends Operation { super(); this.name = "MD6"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "The MD6 (Message-Digest 6) algorithm is a cryptographic hash function. It uses a Merkle tree-like structure to allow for immense parallel computation of hashes for very long inputs."; this.infoURL = "https://wikipedia.org/wiki/MD6"; this.inputType = "string"; diff --git a/src/core/operations/ParseIPRange.mjs b/src/core/operations/ParseIPRange.mjs index 5d50ec2d..38bcc222 100644 --- a/src/core/operations/ParseIPRange.mjs +++ b/src/core/operations/ParseIPRange.mjs @@ -21,7 +21,7 @@ class ParseIPRange extends Operation { super(); this.name = "Parse IP range"; - this.module = "JSBN"; + this.module = "Default"; this.description = "Given a CIDR range (e.g. 10.0.0.0/24), hyphenated range (e.g. 10.0.0.0 - 10.0.1.0), or a list of IPs and/or CIDR ranges (separated by a new line), this operation provides network information and enumerates all IP addresses in the range.

IPv6 is supported but will not be enumerated."; this.infoURL = "https://wikipedia.org/wiki/Subnetwork"; this.inputType = "string"; diff --git a/src/core/operations/ParseIPv4Header.mjs b/src/core/operations/ParseIPv4Header.mjs index d5efc24b..06114329 100644 --- a/src/core/operations/ParseIPv4Header.mjs +++ b/src/core/operations/ParseIPv4Header.mjs @@ -23,7 +23,7 @@ class ParseIPv4Header extends Operation { super(); this.name = "Parse IPv4 header"; - this.module = "JSBN"; + this.module = "Default"; this.description = "Given an IPv4 header, this operations parses and displays each field in an easily readable format."; this.infoURL = "https://wikipedia.org/wiki/IPv4#Header"; this.inputType = "string"; diff --git a/src/core/operations/ParseIPv6Address.mjs b/src/core/operations/ParseIPv6Address.mjs index 1603d47b..3cf9f2dd 100644 --- a/src/core/operations/ParseIPv6Address.mjs +++ b/src/core/operations/ParseIPv6Address.mjs @@ -8,7 +8,7 @@ import Operation from "../Operation"; import Utils from "../Utils"; import OperationError from "../errors/OperationError"; import {strToIpv6, ipv6ToStr, ipv4ToStr, IPV6_REGEX} from "../lib/IP"; -import BigInteger from "jsbn"; +import BigNumber from "bignumber.js"; /** * Parse IPv6 address operation @@ -22,7 +22,7 @@ class ParseIPv6Address extends Operation { super(); this.name = "Parse IPv6 address"; - this.module = "JSBN"; + this.module = "Default"; this.description = "Displays the longhand and shorthand versions of a valid IPv6 address.

Recognises all reserved ranges and parses encapsulated or tunnelled addresses including Teredo and 6to4."; this.infoURL = "https://wikipedia.org/wiki/IPv6_address"; this.inputType = "string"; @@ -147,7 +147,7 @@ class ParseIPv6Address extends Operation { const v4Addr = ipv4ToStr((ipv6[1] << 16) + ipv6[2]), slaId = ipv6[3], interfaceIdStr = ipv6[4].toString(16) + ipv6[5].toString(16) + ipv6[6].toString(16) + ipv6[7].toString(16), - interfaceId = new BigInteger(interfaceIdStr, 16); + interfaceId = new BigNumber(interfaceIdStr, 16); output += "\n\nEncapsulated IPv4 address: " + v4Addr + "\nSLA ID: " + slaId + diff --git a/src/core/operations/RIPEMD.mjs b/src/core/operations/RIPEMD.mjs index fea7116e..00b613de 100644 --- a/src/core/operations/RIPEMD.mjs +++ b/src/core/operations/RIPEMD.mjs @@ -19,7 +19,7 @@ class RIPEMD extends Operation { super(); this.name = "RIPEMD"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "RIPEMD (RACE Integrity Primitives Evaluation Message Digest) is a family of cryptographic hash functions developed in Leuven, Belgium, by Hans Dobbertin, Antoon Bosselaers and Bart Preneel at the COSIC research group at the Katholieke Universiteit Leuven, and first published in 1996.

RIPEMD was based upon the design principles used in MD4, and is similar in performance to the more popular SHA-1.

"; this.infoURL = "https://wikipedia.org/wiki/RIPEMD"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/SHA0.mjs b/src/core/operations/SHA0.mjs index 6982d67c..b83a47d7 100644 --- a/src/core/operations/SHA0.mjs +++ b/src/core/operations/SHA0.mjs @@ -19,7 +19,7 @@ class SHA0 extends Operation { super(); this.name = "SHA0"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "SHA-0 is a retronym applied to the original version of the 160-bit hash function published in 1993 under the name 'SHA'. It was withdrawn shortly after publication due to an undisclosed 'significant flaw' and replaced by the slightly revised version SHA-1."; this.infoURL = "https://wikipedia.org/wiki/SHA-1#SHA-0"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/SHA1.mjs b/src/core/operations/SHA1.mjs index 8f16111c..2dc5ce5c 100644 --- a/src/core/operations/SHA1.mjs +++ b/src/core/operations/SHA1.mjs @@ -19,7 +19,7 @@ class SHA1 extends Operation { super(); this.name = "SHA1"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "The SHA (Secure Hash Algorithm) hash functions were designed by the NSA. SHA-1 is the most established of the existing SHA hash functions and it is used in a variety of security applications and protocols.

However, SHA-1's collision resistance has been weakening as new attacks are discovered or improved."; this.infoURL = "https://wikipedia.org/wiki/SHA-1"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/SHA2.mjs b/src/core/operations/SHA2.mjs index 4b6b379f..05d4c253 100644 --- a/src/core/operations/SHA2.mjs +++ b/src/core/operations/SHA2.mjs @@ -19,7 +19,7 @@ class SHA2 extends Operation { super(); this.name = "SHA2"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "The SHA-2 (Secure Hash Algorithm 2) hash functions were designed by the NSA. SHA-2 includes significant changes from its predecessor, SHA-1. The SHA-2 family consists of hash functions with digests (hash values) that are 224, 256, 384 or 512 bits: SHA224, SHA256, SHA384, SHA512.

  • SHA-512 operates on 64-bit words.
  • SHA-256 operates on 32-bit words.
  • SHA-384 is largely identical to SHA-512 but is truncated to 384 bytes.
  • SHA-224 is largely identical to SHA-256 but is truncated to 224 bytes.
  • SHA-512/224 and SHA-512/256 are truncated versions of SHA-512, but the initial values are generated using the method described in Federal Information Processing Standards (FIPS) PUB 180-4.
"; this.infoURL = "https://wikipedia.org/wiki/SHA-2"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/SHA3.mjs b/src/core/operations/SHA3.mjs index 061810a0..cebb8e23 100644 --- a/src/core/operations/SHA3.mjs +++ b/src/core/operations/SHA3.mjs @@ -20,7 +20,7 @@ class SHA3 extends Operation { super(); this.name = "SHA3"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "The SHA-3 (Secure Hash Algorithm 3) hash functions were released by NIST on August 5, 2015. Although part of the same series of standards, SHA-3 is internally quite different from the MD5-like structure of SHA-1 and SHA-2.

SHA-3 is a subset of the broader cryptographic primitive family Keccak designed by Guido Bertoni, Joan Daemen, Micha\xebl Peeters, and Gilles Van Assche, building upon RadioGat\xfan."; this.infoURL = "https://wikipedia.org/wiki/SHA-3"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/SSDEEP.mjs b/src/core/operations/SSDEEP.mjs index faf3ec3e..4ae5a650 100644 --- a/src/core/operations/SSDEEP.mjs +++ b/src/core/operations/SSDEEP.mjs @@ -19,7 +19,7 @@ class SSDEEP extends Operation { super(); this.name = "SSDEEP"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "SSDEEP is a program for computing context triggered piecewise hashes (CTPH). Also called fuzzy hashes, CTPH can match inputs that have homologies. Such inputs have sequences of identical bytes in the same order, although bytes in between these sequences may be different in both content and length.

SSDEEP hashes are now widely used for simple identification purposes (e.g. the 'Basic Properties' section in VirusTotal). Although 'better' fuzzy hashes are available, SSDEEP is still one of the primary choices because of its speed and being a de facto standard.

This operation is fundamentally the same as the CTPH operation, however their outputs differ in format."; this.infoURL = "https://forensicswiki.org/wiki/Ssdeep"; this.inputType = "string"; diff --git a/src/core/operations/Scrypt.mjs b/src/core/operations/Scrypt.mjs index ec5ce492..134a04fc 100644 --- a/src/core/operations/Scrypt.mjs +++ b/src/core/operations/Scrypt.mjs @@ -21,7 +21,7 @@ class Scrypt extends Operation { super(); this.name = "Scrypt"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "scrypt is a password-based key derivation function (PBKDF) created by Colin Percival. The algorithm was specifically designed to make it costly to perform large-scale custom hardware attacks by requiring large amounts of memory. In 2016, the scrypt algorithm was published by IETF as RFC 7914.

Enter the password in the input to generate its hash."; this.infoURL = "https://wikipedia.org/wiki/Scrypt"; this.inputType = "string"; diff --git a/src/core/operations/Shake.mjs b/src/core/operations/Shake.mjs index 21d993b2..e096ac31 100644 --- a/src/core/operations/Shake.mjs +++ b/src/core/operations/Shake.mjs @@ -20,7 +20,7 @@ class Shake extends Operation { super(); this.name = "Shake"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Shake is an Extendable Output Function (XOF) of the SHA-3 hash algorithm, part of the Keccak family, allowing for variable output length/size."; this.infoURL = "https://wikipedia.org/wiki/SHA-3#Instances"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/Snefru.mjs b/src/core/operations/Snefru.mjs index fe6f2cf4..b7cd65b5 100644 --- a/src/core/operations/Snefru.mjs +++ b/src/core/operations/Snefru.mjs @@ -19,7 +19,7 @@ class Snefru extends Operation { super(); this.name = "Snefru"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Snefru is a cryptographic hash function invented by Ralph Merkle in 1990 while working at Xerox PARC. The function supports 128-bit and 256-bit output. It was named after the Egyptian Pharaoh Sneferu, continuing the tradition of the Khufu and Khafre block ciphers.

The original design of Snefru was shown to be insecure by Eli Biham and Adi Shamir who were able to use differential cryptanalysis to find hash collisions. The design was then modified by increasing the number of iterations of the main pass of the algorithm from two to eight."; this.infoURL = "https://wikipedia.org/wiki/Snefru"; this.inputType = "ArrayBuffer"; diff --git a/src/core/operations/TCPIPChecksum.mjs b/src/core/operations/TCPIPChecksum.mjs index 705d7480..1da73359 100644 --- a/src/core/operations/TCPIPChecksum.mjs +++ b/src/core/operations/TCPIPChecksum.mjs @@ -19,7 +19,7 @@ class TCPIPChecksum extends Operation { super(); this.name = "TCP/IP Checksum"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Calculates the checksum for a TCP (Transport Control Protocol) or IP (Internet Protocol) header from an input of raw bytes."; this.infoURL = "https://wikipedia.org/wiki/IPv4_header_checksum"; this.inputType = "byteArray"; diff --git a/src/core/operations/TextEncodingBruteForce.mjs b/src/core/operations/TextEncodingBruteForce.mjs index 3919dcd9..ee5f70d3 100644 --- a/src/core/operations/TextEncodingBruteForce.mjs +++ b/src/core/operations/TextEncodingBruteForce.mjs @@ -22,7 +22,7 @@ class TextEncodingBruteForce extends Operation { super(); this.name = "Text Encoding Brute Force"; - this.module = "CharEnc"; + this.module = "Encodings"; this.description = [ "Enumerates all supported text encodings for the input, allowing you to quickly spot the correct one.", "

", diff --git a/src/core/operations/ToGeohash.mjs b/src/core/operations/ToGeohash.mjs index 0e7f53ac..7859bd16 100644 --- a/src/core/operations/ToGeohash.mjs +++ b/src/core/operations/ToGeohash.mjs @@ -19,7 +19,7 @@ class ToGeohash extends Operation { super(); this.name = "To Geohash"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Converts Lat/Long coordinates into a Geohash string. For example, 37.8324,112.5584 becomes ww8p1r4t8."; this.infoURL = "https://wikipedia.org/wiki/Geohash"; this.inputType = "string"; diff --git a/src/core/operations/Whirlpool.mjs b/src/core/operations/Whirlpool.mjs index fb3cdfc9..1ef8a11c 100644 --- a/src/core/operations/Whirlpool.mjs +++ b/src/core/operations/Whirlpool.mjs @@ -19,7 +19,7 @@ class Whirlpool extends Operation { super(); this.name = "Whirlpool"; - this.module = "Hashing"; + this.module = "Crypto"; this.description = "Whirlpool is a cryptographic hash function designed by Vincent Rijmen (co-creator of AES) and Paulo S. L. M. Barreto, who first described it in 2000.

Several variants exist:
  • Whirlpool-0 is the original version released in 2000.
  • Whirlpool-T is the first revision, released in 2001, improving the generation of the s-box.
  • Whirlpool is the latest revision, released in 2003, fixing a flaw in the difusion matrix.
"; this.infoURL = "https://wikipedia.org/wiki/Whirlpool_(cryptography)"; this.inputType = "ArrayBuffer"; diff --git a/src/web/App.mjs b/src/web/App.mjs index d001f9e4..1dab16e6 100755 --- a/src/web/App.mjs +++ b/src/web/App.mjs @@ -10,6 +10,7 @@ import Manager from "./Manager"; import HTMLCategory from "./HTMLCategory"; import HTMLOperation from "./HTMLOperation"; import Split from "split.js"; +import moment from "moment-timezone"; /** @@ -515,7 +516,8 @@ class App { setCompileMessage() { // Display time since last build and compile message const now = new Date(), - timeSinceCompile = Utils.fuzzyTime(now.getTime() - window.compileTime); + msSinceCompile = now.getTime() - window.compileTime, + timeSinceCompile = moment.duration(msSinceCompile, "milliseconds").humanize(); // Calculate previous version to compare to const prev = PKG_VERSION.split(".").map(n => { diff --git a/test/tests/operations/Code.mjs b/test/tests/operations/Code.mjs index 4c716546..d9bda06f 100644 --- a/test/tests/operations/Code.mjs +++ b/test/tests/operations/Code.mjs @@ -334,16 +334,12 @@ TestRegister.addTests([ }, { name: "To MessagePack: no content", - input: "{}", + input: "", expectedMatch: /Unexpected end of JSON input/, recipeConfig: [ { "op": "To MessagePack", "args": [] - }, - { - "op": "To Hex", - "args": ["Space"] } ] }, From 8fef01d961b7298240a015363674da2c9adc9e04 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 26 Dec 2018 00:01:12 +0000 Subject: [PATCH 032/147] Fixed bundle analyzer filepath --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 233a2888..1d254791 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -212,7 +212,7 @@ module.exports = function (grunt) { }), new BundleAnalyzerPlugin({ analyzerMode: "static", - reportFilename: "build/prod/BundleAnalyzerReport.html", + reportFilename: "BundleAnalyzerReport.html", openAnalyzer: false }), ] From f6d97c19d9ab842df608847c774c8368eaff84c1 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 26 Dec 2018 00:01:21 +0000 Subject: [PATCH 033/147] 8.17.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ecc63923..f87f5977 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.17.0", + "version": "8.17.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 7bbfb7f1..05212e8e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.17.0", + "version": "8.17.1", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 16be7cb28a47486db90570d200fd93be54fbb1c3 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 26 Dec 2018 00:39:30 +0000 Subject: [PATCH 034/147] Fixed grunt-webpack circular reference issue --- Gruntfile.js | 72 +++++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 1d254791..61576278 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -180,42 +180,44 @@ module.exports = function (grunt) { }, webpack: { options: webpackConfig, - web: { - mode: "production", - target: "web", - entry: Object.assign({ - main: "./src/web/index.js", - sitemap: "./src/web/static/sitemap.js" - }, moduleEntryPoints), - output: { - path: __dirname + "/build/prod" - }, - resolve: { - alias: { - "./config/modules/OpModules": "./config/modules/Default" - } - }, - plugins: [ - new webpack.DefinePlugin(BUILD_CONSTANTS), - new HtmlWebpackPlugin({ - filename: "index.html", - template: "./src/web/html/index.html", - chunks: ["main"], - compileTime: compileTime, - version: pkg.version, - minify: { - removeComments: true, - collapseWhitespace: true, - minifyJS: true, - minifyCSS: true + web: () => { + return { + mode: "production", + target: "web", + entry: Object.assign({ + main: "./src/web/index.js", + sitemap: "./src/web/static/sitemap.js" + }, moduleEntryPoints), + output: { + path: __dirname + "/build/prod" + }, + resolve: { + alias: { + "./config/modules/OpModules": "./config/modules/Default" } - }), - new BundleAnalyzerPlugin({ - analyzerMode: "static", - reportFilename: "BundleAnalyzerReport.html", - openAnalyzer: false - }), - ] + }, + plugins: [ + new webpack.DefinePlugin(BUILD_CONSTANTS), + new HtmlWebpackPlugin({ + filename: "index.html", + template: "./src/web/html/index.html", + chunks: ["main"], + compileTime: compileTime, + version: pkg.version, + minify: { + removeComments: true, + collapseWhitespace: true, + minifyJS: true, + minifyCSS: true + } + }), + new BundleAnalyzerPlugin({ + analyzerMode: "static", + reportFilename: "BundleAnalyzerReport.html", + openAnalyzer: false + }), + ] + }; }, webInline: { mode: "production", From e386863bdb22d6f6bfd696749c26ab41ca7eeb20 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 26 Dec 2018 00:39:38 +0000 Subject: [PATCH 035/147] 8.17.2 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f87f5977..85678c7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.17.1", + "version": "8.17.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 05212e8e..f4f6e25d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.17.1", + "version": "8.17.2", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 8b533e9893e63ff867d3a8fdd0691802e97d6de2 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 26 Dec 2018 16:33:10 +0000 Subject: [PATCH 036/147] Tidied up 'Split Colour Channels' operation and added 'Multimedia' category --- CHANGELOG.md | 5 +++ src/core/config/Categories.json | 16 +++++---- src/core/operations/SplitColourChannels.mjs | 29 ++++++---------- test/index.mjs | 4 ++- test/tests/operations/SplitColourChannels.mjs | 34 ++++++++++++------- 5 files changed, 50 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2f35228..90948ef6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ 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.18.0] - 2018-12-26 +- 'Split Colour Channels' operation added [@artemisbot] | [#449] + ### [8.17.0] - 2018-12-25 - 'Generate QR Code' and 'Parse QR Code' operations added [@j433866] | [#448] @@ -82,6 +85,7 @@ All major and minor version changes will be documented in this file. Details of +[8.18.0]: https://github.com/gchq/CyberChef/releases/tag/v8.18.0 [8.17.0]: https://github.com/gchq/CyberChef/releases/tag/v8.17.0 [8.16.0]: https://github.com/gchq/CyberChef/releases/tag/v8.16.0 [8.15.0]: https://github.com/gchq/CyberChef/releases/tag/v8.15.0 @@ -150,3 +154,4 @@ All major and minor version changes will be documented in this file. Details of [#443]: https://github.com/gchq/CyberChef/pull/443 [#446]: https://github.com/gchq/CyberChef/pull/446 [#448]: https://github.com/gchq/CyberChef/pull/448 +[#449]: https://github.com/gchq/CyberChef/pull/449 diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 66c2a9e9..686c9842 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -347,9 +347,17 @@ "Detect File Type", "Scan for Embedded Files", "Remove EXIF", - "Extract EXIF", + "Extract EXIF" + ] + }, + { + "name": "Multimedia", + "ops": [ "Render Image", - "Play Media" + "Play Media", + "Remove EXIF", + "Extract EXIF", + "Split Colour Channels" ] }, { @@ -366,10 +374,6 @@ "Generate QR Code", "Parse QR Code", "Haversine distance", - "Render Image", - "Remove EXIF", - "Extract EXIF", - "Split Colour Channels", "Numberwang", "XKCD Random Number" ] diff --git a/src/core/operations/SplitColourChannels.mjs b/src/core/operations/SplitColourChannels.mjs index fb3e3d6e..f11ca14e 100644 --- a/src/core/operations/SplitColourChannels.mjs +++ b/src/core/operations/SplitColourChannels.mjs @@ -24,25 +24,12 @@ class SplitColourChannels extends Operation { this.name = "Split Colour Channels"; this.module = "Image"; - this.description = "Splits given image into its red, green and blue colour channels."; - this.infoURL = "https://en.wikipedia.org/wiki/Channel_(digital_image)"; + this.description = "Splits the given image into its red, green and blue colour channels."; + this.infoURL = "https://wikipedia.org/wiki/Channel_(digital_image)"; this.inputType = "byteArray"; this.outputType = "List"; this.presentType = "html"; - this.args = [ - /* Example arguments. See the project wiki for full details. - { - name: "First arg", - type: "string", - value: "Don't Panic" - }, - { - name: "Second arg", - type: "number", - value: 42 - } - */ - ]; + this.args = []; } /** @@ -55,18 +42,22 @@ class SplitColourChannels extends Operation { // Make sure that the input is an image if (type && type.mime.indexOf("image") === 0) { const parsedImage = await jimp.read(Buffer.from(input)); + const red = new Promise(async (resolve, reject) => { try { - const split = parsedImage.clone() + const split = parsedImage + .clone() .color([ {apply: "blue", params: [-255]}, {apply: "green", params: [-255]} - ]).getBufferAsync(jimp.MIME_PNG); + ]) + .getBufferAsync(jimp.MIME_PNG); resolve(new File([new Uint8Array((await split).values())], "red.png", {type: "image/png"})); } catch (err) { reject(new OperationError(`Could not split red channel: ${err}`)); } }); + const green = new Promise(async (resolve, reject) => { try { const split = parsedImage.clone() @@ -79,6 +70,7 @@ class SplitColourChannels extends Operation { reject(new OperationError(`Could not split green channel: ${err}`)); } }); + const blue = new Promise(async (resolve, reject) => { try { const split = parsedImage @@ -91,6 +83,7 @@ class SplitColourChannels extends Operation { reject(new OperationError(`Could not split blue channel: ${err}`)); } }); + return await Promise.all([red, green, blue]); } else { throw new OperationError("Invalid file type."); diff --git a/test/index.mjs b/test/index.mjs index 387b6e7f..9fe0407e 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -74,7 +74,6 @@ import "./tests/operations/SeqUtils"; import "./tests/operations/SetDifference"; import "./tests/operations/SetIntersection"; import "./tests/operations/SetUnion"; -//import "./tests/operations/SplitColourChannels"; import "./tests/operations/StrUtils"; import "./tests/operations/SymmetricDifference"; import "./tests/operations/TextEncodingBruteForce"; @@ -84,6 +83,9 @@ import "./tests/operations/Magic"; import "./tests/operations/ParseTLV"; import "./tests/operations/Media"; +// Cannot test operations that use the File type yet +//import "./tests/operations/SplitColourChannels"; + let allTestsPassing = true; const testStatusCounts = { total: 0, diff --git a/test/tests/operations/SplitColourChannels.mjs b/test/tests/operations/SplitColourChannels.mjs index ce91cde5..9999aff5 100644 --- a/test/tests/operations/SplitColourChannels.mjs +++ b/test/tests/operations/SplitColourChannels.mjs @@ -11,16 +11,24 @@ import TestRegister from "../../TestRegister"; const testCard = "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"; const testCardSplit = "iVBORw0KGgoAAAANSUhEUgAAAUAAAAC0CAYAAADl5PURAAAhfklEQVR4AezBP49maZon5Ot+zhsRVdXTO03zRyxidxnhIKQRHgjhgMMXwMblS+EigYGBhYMQKwwsLLDQaqRFsAxCC730dE1lRrzn+dHSvejM2YjMyqzMrIiseK6r/lvyZxiIx+LLmtpFu2qbFgQDQeEev0Lh3vvd+7Ki3WubdtHutL/AX+A/8XX5Fj/gou1elkIQZ/+R9p9qb7Wrs+F57SgUgt/gH+K3KIddG9qubZ5HtF8j+C2mr8/lz/Bn3i2+rKlt2q5t3u0NvtHuvV/5sqLda5t2cfY9/oBfofANJsrzilbOot3hHr9BsCMO5WV40K7an2q/1m61q7PheU2Uw5/gV/iVs10b2q5tnke0X2HHN7jiAcGG8m7lZbgEQTkE5eUKguDi08Sn2bRNK2dB8IC3+B6Fe0xcvQylxdm32PE7BNPLUlqcTe2iXXw9fos/9XUIJiYm4hCH6Wxo0/O6BPG0eH7RyqFQiJdvICgUCoVCeTlKi7Nd271M8bSpTWellRbPqzwWX5eJielQWrxsF/+MQrwsQSEoh0J5GTZPmwh2TATBWy/TcDa0W+2qlRbPa3ra0Ib3K88rWjkEQTlMbWjx/IKBDYWBiUIQ7za1eF4X/4wgXo7yZcWnKR+mUL5uQRCUVl6moBBn5eUJymFiojC0OIufR1Aei0OcBRPx8l2CoBCU16V8mmhX7cZZoVAoL1c8rbSplVaI53dBYWrRhrNdm9rwMgwEQWnxWHke5WnlMFAorbRCvGyXQmmF0grx/KZWHgvKy1aIxwrxcpSnxWOFQiGeX/yyFArD1yEIogXl63CJs2jxMpTHSivE85patGjlUNgwtMLARLwM09N2bUe8PNO7BaVt2qaVl2vDcDacDS/H0MpZvHzDsizLJ4iv18VXLj5N+TSbs/JYsGPXgl0bntfUytM2PGBDsDsbntf0bnEoL9OuDZS2Y6JQ2tSGNr0MA+XrNSzLsrxSF1+58jKUpwXxbtPLEE/btR3x2PSyDJRWDruXafPYhuFsOBteluHrNCzLL1Qsy/tdvHLl00TbnZWzQmmFDRPTy1CeNrSBYDobntd0NlEI4jCclRbPK1o5TI9NbWhTG5ZPMSzLsrxSl0K0QiGeXzxWPr/4PC6eVihn8XLF+xXiML088ePi6zYsn8OwLMvyCcrXa1iWZXmlhmVZlldqWJZleaWGZVmWV2pYlmV5pYZlWZZXaliWjxDL8ssxLMuyvFLDsizLKzUsy7K8UsOyLMsrNSzLsrxSw7Isyys1LMuyvFLDsnyEsiy/HMOyLMsrNSzLsrxSw7Isyys1LMuyvFLDsizLKzUsy7K8UsOyLMsrNSzLR4hl+eUYlmVZXqlhWZbllRqWZVleqWFZluWVGpZlWV6pYVmW5ZUalmVZXqlhWT5CWZZfjmFZluWVGpZlWV6pYVmW5ZUalmVZXqlhWZbllRqWZVleqWFZluWVGpblI8Sy/HIMy7Isr9SwLMvySg3Lsiyv1LAsy/JKDcuyLK/UsCzL8koNy7Isr9SwLB+hLMsvx7Asy/JKDcuyLK/UsCzL8koNy7Isr9SwLMvySg3Lsiyv1LAsy/JKDcvyEWJZfjmGZVmWV2pYlmV5pYZlWZZXaliWZXmlhmVZlldqWJZleaWGZVmWV2pYlo9QluWXY1iWZXmlhmVZlldqWJZleaWGZVmWV2pYlmV5pYZlWZZXaliWZXmlhmX5CLEsvxzDsizLKzUsy7K8UsOyLMsrNSzLsrxSw7Isyys1LMuyvFLDsizLKzUsyweKZfllGZZlWV6pYVmW5ZUalmVZXqlhWZbllRqWZVleqWFZluWVGpZlWV6pYVk+UFmWX5ZhWZbllRqWZVleqWFZluWVGpZlWV6pYVmW5ZUalmVZXqlhWZbllboMDK208vPZtF2LtjsrDI/F8yqtnE3tAYWJqRUKG6bnVVppcbZ5v+F5TY8VJiZ2bddutfg8pk+zaUFpw2FqQ5va8HIUrtgxtOnrMPxRfF3K16O0QqF8XaLF1yOI5ecShyC+Hpd73KNQKMTP50bbtHvt1tk9gs3Z8LzutRtn99odCre4RbTdyxAtzsrZdHaD4Op5DRSi3Wkbgl2LdtUuPo/Np9m1zWFiavfaN9qDdutlKBRuseONs3J2q1216XldLF9cfL3Kyxe/XOWstPLylK/P5QY3CAqFeLfh83rQbrV42kRQmFphel63nrZppW0YDtPLsGnT2abF08rLMJ39oN0huPW0B23zaYZPc9U2Z7tWvg7BxEW7atFKe+tlucSy/PLE+0WLTxNfVpzFyxQE8XW5DAyU53Gj7dqds7faN1owMBGU5/VGu9NKu2gThd/jr7w8u6ft2qZ9h+AH7d7LdIeJ77Vo5WxqNz5N+TR3WlDaLW603dmts93zu/fYtyj8tRYv08U/FZTlc5soRCsUplbeL55H+ToU4t3K00orL9twVs6G5xWUs0KhvHz1d8gdpsNEtDgbPq8H7U773tl3CP4KhQ2/wl94GaKVNrXh7H/Hv4p/gIk7TNx7XndaaW+1aH8X/yX+XdziFsFFe+N5lbO/1v5z/Hv4j7W/pU3tjbb5NNOnucem7fgP8Z/hzzGwa5u2a9HK8/or3OG/wRUXDPweE3+i3WvfaVctntflH+EO02EinjZ8Xg/anfbG2S2CB4dfIwjuvV98Wbs2tF3btB0Df63dYOA7BD94v/iy7rTShhZtQ2HHAwrlsHtehaC0HcGOHRftok3tom0+zfTpCgMTQ/vHzjZt1+JluMcdfoMb3Gm/RuFPtHvtG21q8bwuE28Qz+OiPWibtmv32h2Ce2wICt94XtHKuwW32r/mMDE8r6kNZ1Mb+Hv4N5ztCIbnNTztBjf4P7R/pJUWbfg08WkumNi1v9R+h8IPXrYLvsEFNxgI/hYKU5ueVp7XxVektIkdA5vnVX6aIJ5fnEWLQ3msUF628vINxNM2j5UWL8PAwMTUymFow9nwMlz80UC06ed11UqLp+0OwY0PE19WadGilTYxcdUmJoYWrTyPzVlpm8NEULhiOAzP61671Xbtgg2lRbvR7rXpeb11Vg5XP27zvO7xBvcYuNV2BOXswdnmeV38URDPK95vIlp8uPLzKGelFQZKC4Jo8X7xZQ1Pm9rQSiuHeBnilyPaQPy4eF6FG9zi4mxgatHiLJ7XxR9t2B3ix5UWn8dFu2rD2XQoh90hHitf1tB2rbRdu9W+0aa2aVOLVs7Kl3XVLs6Gsys2TBQu2vS8bj3tih3RSnvweZVPE60QlPYG5bE4m55XYWLDhmibNrSp3XpZLv4oWny4+LziLFpQKC2IQzmUFi9PeVohDvHzig8zUZjYLF/CwNDi61AoFAbK1+XijzZMBOVluEUhmHjQBm61oLxfeVpp8WlKu2rfaJuzt9rAhiB+mvLh4v2Gp01tYEdwxZ2zoDytUH5em1YoXBDsWhyGQ7Q4G54WLSgUSotDUJgeG4i24YobLYjD0Ka2OQTR4v0KcRja9LRyKC2IFgxc8Y22Y3N262lBYdfibHO2aw8IvtV2rVAorbzfxR9NTD+PchZtIB4LylmwY0M8NhziLFo8rfx0QZyVdqeVVlo8Vp4WLQ5xVj6PcrgguOLO2XAWhyAoj5UWrXy6iaEVBiYKhcLuMFFa/DTDIQ7x46bDFd+iPK2cxWOFeL94v/K00uKscMUFEwMbJoYfFy0olFZaadGiRSuttCAYWnm/iz+6ojCw+zClxceJpz1opb11tmHHd/gWOzaU9ytn5cu41Up70Da8xffajl3bMHy48vm90TbvdoM7XBzuceuxclber3weE4Ud93jAjl271XZnpcXTpveLHxdPKwzs2g8OG3YMrbRyNh3iUNpwFi1anMVZeb+gcMU3eIMbFKazoLSJ4Ea7aNHKWWk32o2zTYsW7eoszi7+KBgoHy6+jNLibNeCiQ0Tw/vF+xXi3cpZnJU2tdKmVhgY2g2uKEwMz6sQj0UrFAoXh+Aetz5OtPL5FR5QuMONs3gsnk8wsGPDxNB2rbTSSitPK620OIsWbTiLD1PaDSYumLh1mChPK5THyoeJVs4KQbThLM4u3mFgeqwQlA8T73eHYGq793uDDRvix5UfV94tHiuPRSvtVrviDjcOF4f4eHEY3m86lMc2xCFaHHZcUdi0O8THK5/XjoGBHYWBb3DjcO/sO0y89fMoBEMLBh60XXvrsDlcEY+VFi3OLtrU4ixaPG0gKGfRgu+wYWjRLoizIM6CqRUKQbRCedrVoVDO4mw4u/ijwsTUCvG0aNHKIT7e1OJQiDYwHXZEK8ShPBaH8liclbPyftGmFpTDBcHV03aUx0qLVp42nZWzeFpp8bRyGChsDlMrz+uKO0S74AH3uPVuVww/n2hBtOmxW23DjqHF0wpBIZ4WRIuz8rRo0aJFi7bhLUoLytMK5bHC0EorRCvvVs6CaEE5i7PLLX6lBYXSJkqLFq0QDC1aHILhEC0Ope3avXbRfoOJ32PDFb/F/4xv8b1DPLZ5LA6lRYuzDXGIsxvtQbtFEG3HxO/xd/DXKOwo3DjEIVp5WrSLQ7RoQTnEYxPBLYJ7BFftn8Nf4n/Aju/wgCsGvnEWFKIV4jC1aJsWLYjDcIjHNgR/0Ep7i9/hP8DFYccVwbf4d7TpacNZtF170HYU7rUH7S0K99quXVF4iyt2XPGvaL9C4a32gGA6m54W7cH7Te83vd8b/J/4+yjcoVDYcEFwo32jbZi40S7aBYUbFAYKdwjutKFtPs3lv8C/iFtMBNEK0aLF2XQI4hCHYGIiCIKJ4EF7g2BD8FsEf8AVP6Dw5z6/YCIIgqlNBMFEtCC4x8QFExPBRPCA/wn/C4J7BBcEQTARBMGOIJiYCKb2gCCYCCaCYNeCiSAIggcEN5h4g+ABwT+Pv8K/jx13mHir3SCYCCaCiSAIgmAiCIIdQRBMBEEQj8Vhwz0eMPEG3+G/xn+P/877vdWmpw2ttKntKNxioHCLwq224QY32HCHDbcILghu8IP2b+PfxL+FHb9H8AMe8D2ueIOJ/xUTP+CK77HjAcFVmwh2FKb2DQrBQDBQKGwoFAYmyuEBA/8VJv6AHd/iewRvEdxr97jirRYU7jHxgE37BgPf4YJf4xZ/ggt+hRv8KW7wt3CDP8UNfqUNbTi7/Dn+nrMgCCaCOARB4aoFE9GCiUIQTATBRLBp95h4i4lbbUPwBsE/wcT/iImJYGIimAiCYGIimAgmJoKJHUEQ7JjaxEQQTExEu2JHMLFjQ3CDqU18j+AWE28QBEEwMTGxIwgmdkwEQRBMBBMTExPBRDARBBPTobBjYuIBE/8AO/4CO75HsCO4RzAxMRFMbSK4IlowEQQTE0EQBNEmJqIFQfAdCr/HxG9QWhzuEOyYCP5l/H2f5h432HHBjg0PuMHEwBvcYccFE8NhYuABb/H/YMc/xBW/wwP+L9zj/8UD/jdc8Ttc8Y9xxT/BjgctWmEgKOw+j0IwMLFhd9iw4wYPGJgoBIWgENwg+DVu8Ftc8Ldxgw23+Ndxh38BF/xdfIO/jQt+o5XHglEo7xeHIFp8uHi/aPFYtHi/0sq7lUOhUCgUSiuUViiHQjmUHxc/TaFQWqFQKJ9HvF+hMFAoH66cBUEQX0Y8Fo+VLy9aeb+JYCII4mnlEIcgiBY/n2jRopVWfpqBgQ0bBjYMbB4rH+eyYUO0wtSCYCJaEASFQhAUogWFQlAIgqAQvEGwaRcEt4jDBcG/hIm32oZgIpgIJoIgmJgIJiaiTewIgomJiWBiIpgIJiaC0oKrtiN4wEQhCIIrJgamFq1QKGeFQiEolFYICoVCaaWVQ6HwRivs2BDcYWJi4g8IdgQ3CAaCiYmJYCIIJm4QBMFEtImJIAiCIAgmogVBUNoFE1ftiuBWu3d2wY3D1KKVVtpEYcfApt1qF6200kobKESLdsUFF21i4g2uuMPAd7jiOzzgLW7wHa74g7MH7IizYHcYKBQGrhiYGJgYCApXFIKB6awQh4EdQ9twxYaJaHE2MXGP4KoFExsG3mo7ymFgQ3najmD4p8rTCoXSCoVyKIdCOQTxYaIF0eIQxKH8NIVyVg7l3cpZnEUrh2hxFh8niEN8WYUgDuUQPy5aoVAoFMqhtEJ5rFA+TBAtiOcTrZxFK2eFQjkrrbTSosWXFR+mnJVWfpogCIIgCOKsUA7xbtEu97jXgg1TizYxtSCINrWJINrE1IKJaEEwcUFwg4kdA/daIfgewQ2CP0XwfyOYCCaCiSAIJoKJYCIIJiYmgmDHRDAxEUwEExPBxA0mNkSbeMCOHaUFV+zYtDjEobSgUFqhMBFPKwSFOATR/kS7YscDgrcIrgjeILjRgiAIgmhxVohDtEJQiA9XiHZFEO0HBNOhHL5DULj1WHlaOQsKb3CDe9ziijvs2FDaRbvRLtoVhaFF+xYb7nHBBdEKb/GgRQsKhQsKV4dCMBAUpjYRTG1qU5sOhWi32DFwxYYdAxNTe6vdI3jQvsEVA/e4YEehEARX7YqBiTgEwcSOB+1e27SpTW34G8pZOZSnlaeVz6N8PvFh4vMI4seVd4vHosWXU1oQP11ppQVBMJ2VQ3la+XFxKGdBED+P8n5xFsSnKT+fIN4tPk75ePG0aOXdLje4cShMZxPRgoloE8FEHCZKCybiMDExELzFxMDEVbug8GtM/DWCv0TwLYJCUAgKQRAUCkFQmFpphaBQKAQDExMDE4XCWwQTE7sW7HiLiUIwEGzajmBHMBEEEzuCiYlgYmrBxMREMBFMTAQTE8HERHCvTUxMBMHEBUEh2DDxgOCKYGJqE0EQBMHUgiCYCCaCIAiCiSCIFkwEE8EdgsLEQOEHbXP2A37nMLxfOQyHG2y4wYbdWVDYseEBN7jHLb7RJgpv8YAdE29wxRtcsWHHjfZXuOJ7POAeV1yxIw7RdmfRdm1qU5tatGjBgza1XZtaIbjgigvucYMHvHF2xdQmgmDDhjvc4Fvc4Rvc4g63uMENBjZsKAxtc3bxI4IgWhDEh4vPo7xbIZ4WP64QjxWiFYLSyoeJDxOPlVYoBIXS4udRWqFQfppCaYXpp4sWxMeLL698nPhliJ8mzgqFQqFQKJRPd4lWmAiixSEIgmCiEARBtCBatCAIgmhXBEOLdqNdMTER3CDYEEwtCIJoQRBEmwiCaMFEEARBEExMBMGOieAGQRyCqd1g0yZ2TK0wMLFhYEcQhzibGAiGQ2FqEwOF6RAEhUJholAYmJgoTEQLHjARBBuCQiEoTARBEG06BAPRgiAIogVBtGiFicJ0CK4ICsGDs4HvMD1taEFhahPBQDkMbWgDhR0bokW7aBMDA8HAhmCgtA1BEBQKv8IVf8CmBRPxWGFgojC9WyEoDAxcMRAMTEwMTI9NbWpX7UErBDd4wAVXbeAGF1y1N9hxr10xsGHDPSauKGzarm3Ohj8q7xdn0eJ5xfMqj5Xl/xc/j0L5ZSiUFkt8vPLhLuX5xPOLVgiCOBRKKwSFQrxf+TIK8dPEWXzdCtEK0UqL9xver7ThUChtx0A8rbSLdtGGVg6FCwqFoFAIgh07rrjiiiuCoFAYCOIs2LWgMLCjEBSCaMGOXdu16TC1aIXggitu8BZ3uMcN3iLag/agxdmGDRdccItbvEXwgMLExMDw2O5sWJ4UlENp5ax8mFh+DoXy48rycyhnpZWfJs4KhfLTDH9DWf6m+DiF8uPKL1sQP69yVn4etyhs2tDK2VW7arunFQqFQqE8NjEdCoULLrjBDS64YGDThkOwa9Hiw11QuGgXbXN21XbsuEdwo92isGmF0uIQBDt2fIfv8C2+wx3ucMHFYWBgYGBgYFi+mPJ84iwei0Ocla9f+ToFhfJ1Kj+faEF8vItFEEQr7xZn5acrX0a0eFp8mPL1iJ9m935BYWo7CoVbvMUNrrjBA+5wr03cYMcFE9PhLW604AFXTAQDAxftgolbFK64YseOHTuu2DE9rVCYGJgOhWCgUNrAxMDEwL121e61B+1B27UHbdeu2hXBrkW7ajsKO3bs2HGv3WtvMfHgaeVseOXKWTnETxM/rvw0pRXKWSwfozy/QjnEoSwfKwiC+HGXQhCUw8AVA0G0ICgtnhaHIJgY2LFhIg6FOAQbrigEQSEYmJgOhSDOSistiFYoFOJQiBZnEwMThSAICsFEYWJgaoUrLrg6xFmhsKMQFIKBB5QWh3JWCAqFoBCUVlpQDnEWBMH0WDxWCAqFaKXFWbSB3fsVJgpBIZjawAMGJiY2RNt8mE3bUCjtBgO32kW7wQVB4Ua7wXC4YDhszq64YseOB+zYccUNChcEAxuCeNpEOUxn0aazgYmBiYFCMDBxwRU3eMANHrBhx8B0uOABGyYKcdiwYcPmLCgUNmy4wQ02bB6LNrURFAYKpQWlFUorlBYEE0EcymFgw4aBYOKKQqG0QqFQuGJqAwOlTcShtEKhEAQTQRAUhncrFAYKA4XSCvFuhdI2DAyUw45CoVAoZxPRCoXhMBHEWaEQhyDOJiaCiSCYmBgOhYGBwvBYOZuYmJiYCIIgCIIgWrCjUCiUQ6G0gQuuiHbRpkNhQ2nl48XZ1KKVtmmllVbOylmhHAYGCqUFQVAoFMqHi48XLdpEaeVsaldtanFWKO9WKBQGylmhHIIgiB932RFtahNBIVoQBIWJHcNZtGjBRDBR/r/24GbHjus6A+jap6r5Y8eGBWSSx8xbZZxZHifTBEYSyrIp8tb5gmAjOLi+l81uUqJIqdYiKGw4tGBiIpgIdkwtiFaIJQiCIAgGgtImJoKJYCIIgiAIgosWTASFiYkgCIIgCIJoQbRCIQiCIK7tKESbiBaUNhBtaNGCgaAwUJYgKExMlBYE0xIEQWmF0goDQSEoTC2YWjC0aMHERBAEQRAEU5u44BXe4y1+j6kVohWC+DSFskxtYsPEwMRAUAgKEwNB4YIHHAje4cAFF1xwQRBsCF5ohcLAhg0ThUJ82MRAYWLgwMBEISgE0WKZ2uHawIENFzzgHTYc2HDgPYL3WlzbseMBOzbs2LDhwMTEgULhAS8sUztQmNpeWqFQ2sTABcHERGkT0QqxBEGwaQcKhYFCYSDa0GKZWhAEhQ2FIChMbaK095gYKJRbhaAQFApBYWgHgolCfNzQgmhBIRgIJgqFWIJgIpiWgQPB1IKJiaAwEUwcmAiCYYklmG7FUpgIokULYgmCYCIIphZMRDswMXzYwIENByYK/4D3lkIQbaIwEC2ulVZaUJgolPvKtdJK27TSNhQKhYFgYEOhUCjXCoWBgfI8hUKhUCgMBIUgPl20uFbahgs2HJYNwYYNhbJEGyjLxMSBw32x7IWLFpR2oDBQOFAYWmlTGwiiDQSFDYWgMBFMTC2IpRBtQ7QLJoKLFgQTQRAEG8oSlFaWQlCIVlppE4VowUAwUSgMTEthYmiHFteCIJgIBgqFTQumViiUa2UpDAQDhUObWhAtlguiBUEsAxMDEwOxXFwrlGVDEESL5UC0IAiC4AUOvMZfUSj8iAccKNcKA0E8XVAISnvQhmsHNhzY8RYv8RYvccEDSpsoTExMTEtZpuUdDhQKr/GA7zAxtYlCobRCtImBiaEVBgrBwIGBicKPCF7iPR7wDg94jx1v8RI/4gV2bPgR0TZMFKZWKG1iYiK4YOAFXmBgs7xH4aJNbdM2bf9XfIegUNrEhgMDB4INQbBjIG4FQRAEwYYLXmilRQuCaNEmgkPbMS1BtCDagYmJaA/YMFEIgmAimNpEULjgNQ4MBAeCIJgIgiAIgpfYUdrEoQUbgiDagYGBl5gILtrARLRgIggmCjuiFQ7tQCxBEAQX7NgRTEtwIJiIFgTBay0IosUSLQiiFXZLLEEw8Q4vtcI7/B7/gjdaUJgIgjf43rVope0IDu2wDLzFjr9iww8YeIMdP2LD99jwF2x4h4FC4Z32PYL/QfCfuODPuOA/cOANDvw7gr/hwPeIr88P2g/ue6dFO7R3GPgbNvwF7/AKO/4bD3iFHRMPCHa81aa2abu2/zMKQSEoBKUNTG1govAamxaUW6UFExsKf8QLPLgVS2FgolB4gT9iQ7RopcUSrfASf8BLBJsWLVq0oFAI/hGFgcJmCWIJNkwMbNixoRA8+LBCELzEdxgoTAwEQSyxvMJL/B6bNnBgeFzwgN/hlTZRKASbFi2W4J98XLSBIBh4wB8QS2lBMLSgtAs2/BveYCIYiBa8wZ/cV9pAEBSmawOFw32lDe1wrVyLazsmSjtcKy1uFYYW16LF05QPKwzEEgRxq7AjuGi7dmjR/kv7s2u7dtEetPeWYZnarj1o+9DiVrSJaEG0gd9pQbk1sWkTpb3GK0yPCzZMlDYwMCxBuRYMLQgGNmw4cGjlWqFwoBAEO4ZWOLSylFYIJiamNjEQDC1uBYVgIpjaBbsWrVCWwg8IXmnRDmyIW4VoP2LDgxYUChNTi1auvdfivoEDhYGJic1SbhVKCwpBYdcmgri24cDh4+LzRIvHxX1TC8rTlRa3CvF5ohWC6Vp8WBC3CoX4fOVx0fZpiRb3FaYW7JZy30AQlGXTyocFA7EUgs21cqsQrVB4jw2HVj4sKJRWliAot+LaQLAhGFohPm4gKK2woRCUJZZgR2FohYlNK/eVNvCAWKIV4lpcmx43ERQOrRAU4nHlVlAYiGVqh4+LFkvcmp5mui8eNz0u7osWt8rzxX3R4r6yxHJx7eJ5Lq69dy2IWxftog0/s7gvPq6cgvh1K6cvKYgvK75Ou88UX0b5ZZSvR1C+LeV5yvPE6VPFzy++bsMXFC2+rPj2xS+r3IrPV1qhLIVyOv28hmcqvz1B/LTKtyt+WuWnE6fT0w3fqCCeLn46hfLbUCin06/T8AXEEt+2cvo/8XWJ0+n5hi8kPl359Sinvxen0y9jeIJoQWnl6QqlleeLz1NaadPTBYXSYonnC4J4ukJp8WnKMjxPUJZC/DSC0oLSpucpt+J0etxwOp1Ov1HD6VcvTqfTPcPpq1ZOp9PPZTj9ZgRxOp3+33D6ZsXTxel0+nvD6VctTqfThwynr145nU4/h+ELCGIpFMrTlFaY2BDPFxQKQWmFQqFQKK0QrRBM9xXKtdIKpRUKQaFQ7itMlLYhnm4gGAgKQTxNeVyh3CqUpVAoFAqlFSYKQXm+WKINp9Pjdt+AWMpSKC2ulSWulWvxYYWg3Be3YgmiBdFKixb3TQRBEM8TRAuCINrweab74loQS7RNK5RWTqcvY/eZ4nHlVrT4uKDcKpSlfFhZSisUgnJfUJbSSitEK0u5b2BoA2WJxxUGBqKVjwsGBgYGgqBweFwhCKYlKGweN1yLFm2iEC0oHDh8mnI6Pc3uZxaUJVo8X7RCobR4mtLKsnlcUIhWWiEYlnJfUNiwawNBLHFfsGHDjmiF+LgdO4KhBYUHjxt4jVc4LMHAYYmltM21cm2gEAxEu+CVpwnKrXI6Pe5/AXt+L94J8N2HAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAAUAAAAC0CAYAAADl5PURAAAjzUlEQVR4AezBwY5maZ4f5Of/ni8iq7qY7vFoLDRIlj1CXAASO18C98FlcCVsESztC0Cy8cJbhFjakr0ALEaA7J7uroz4zvtDPX+1DscRGZVZWVkRWd/7POV/En+NgXgqfh5TG1q0QhAEGx7wiML0sgc/j02bWrQ77Vf41/hzhx3ldQ1ncfYN/gabFq20eH3B0C7aP9P+O23TLlppu9dVKEwE3+Lf4D9BIdrQopUWryPa9wgKE4VgolBatNLiTbj4a/y1D4ufx9SGFm1oE8HA93iv7V5Wfh6bNrVoU/sGf4vfoLSJ8rrKWZwVHnBBEATlbYhW2kX7jfZn2qZdtNJ2r2ugsGvf4Dt8p0UbWrTS4nVE27BrVxSCiUJp0UqLN+EiCMohKG9LOQQTwcXnKZ9n10obzjYE97jgDoXCxOZ1RYtWWmn3+B4XRAtKi7chzi7a1HbtQYtWXl8hiPYHDGdDi7chWmFix9VhauV50cqrugjiefH64qlCId6+gaBQKBQKhfK2lFbajh3ledPri6emNrU4i1beliCYKIdo8TZEK0xMTBQGylfh4j9WiLclWjBQKAzEy8qXVVpp8WGFQqG06XVFi1ZaaUOb2tDibRjOJgqbNrSplTa18rpKCwobNtyhMLWhxdsRbWBiYKBQCOKsvCkX/7Eg3o7S4qwQPyy+rGjR4nmFwkBpA/H1CII4lNcVTwVBIVqcxdtQCKIFExOFaNHi5xWUp+IQh2gTQZyVN+UiCApB+ToUCvGy8rL4acXZru3YsaNQmCivaziLVg7RCkG0YHhdO8ohDnHYtKFFi7ejfLryZZXnlRYUCoVCoVCIs6FNrbyqi0JphdIK8fqm5wVBedsK8VShUF5XeV5p8VRp5W0IytevUCgUBnZv20QQxFfnIs6ixdtQDtEKhUJ5XaWVNrTpbKA8Vd6GeN7AhoFgICgtXtdAIdpEaUG0aFOLt6G0IJgIgoloU5vOhtcVFOIwtTiUFq20eFXDsizL54hDfFUubl18GaWVs4HC0Havq7Q4K60wUQiCOMTrGiiHQmlxiLNo5XUNbaIwMFAolDY8r7yuIJ4qrZyVFq28qmFZluVGXbwV5XWUzzO1aPGyaEEwvK7S4qy0gYHSSitvw45yiDYRlFZaeVumFgQTExOFaNHibQqC0oL4sHgThmX5mpUPi2V50cVrKy1eR3ye0korL4sWBOV1RYsWrbSBIIin4nUNLQ6FQiGeF29boRxK25xNb0OhUM5KixYt2vCqhmVZlht1UYhWKMTPJ1q04SxaOSuUz1d+GlOLVloQh4FCEJS3pbTyYUFQKK8rKMQhDtGGFq20eBsKhUI5RIsWb0uhEASlBXEY2tRKi1c1LMuyfI7y1RqWZVlu1LAsy3KjhmVZlp9SfDWGZVmWLyHevGFZluVGDcuyLD9W+aoNy/IpYlkO8WHlzRuWZVlu1LAsy3KjhmVZlhs1LMuy3KhhWZblRg3Lsiw3aliWZblRw7J8irIsvxjDsizLjRqWZVlu1LAsy3KjhmVZlhs1LMuy3KhhWZblRg3Lsiw3aliWTxHL8osxLMuy3KhhWZblRg3Lsiw3aliWZblRw7Isy40almVZbtSwLMtyo4Zl+RRlWX4xhmVZlhs1LMuy3KhhWZblRg3Lsiw3aliWZblRw7Isy40almVZbtSwLJ8iluUXY1iWZblRw7Isy40almVZbtSwLMtyo4ZlWZYbNSzLstyoYVmW5UYNy/IpyrL8YgzLsiw3aliWZblRw7Isy40almVZbtSwLMtyo4ZlWZYbNSzLstyoYVk+RSzLL8awLMtyo4ZlWZYbNSzLstyoYVmW5UYNy7IsN2pYlmW5UcOyLMuNGpblU5Rl+cUYlmVZbtSwLMtyo4ZlWZYbNSzLstyoYVmW5UYNy7IsN2pYlmW5UcOyfIpYll+MYVmW5UYNy7IsN2pYlmW5UcOyLMuNGpZlWW7UsCzLcqOGZVmWG3Wx3Lb4eNHKsvwiDMuyLDdqWJZluVHDsizLjRqWZVlu1LAsy3KjhmVZlhs1LMuy3KhhWT5WabEsvwjDsizLjRqWZVlu1LAsy3KjhmVZlhs1LMuy3KhhWZblRg3Lsiw36mJiolAYiBZEK1/WcFbOJgau2LVCvK6pRYt20TYUCoVHFC4YPl/8NHbt4mwgCKIVLgiuXldQzgqFwtCGdtWilc9TPs+uDQRBEASbdtU2bWrD6wp2DATRBgrRdq20oU2v6uKPgvL1KF+P0gqFclZeFq8rWpwF8TYFsfxc4hBflYs73KEcys8n2tQ2bWrRNm3DxHvE67vXdmdDu6KwY8edFi1e16aVs6kFwcUheEBw53VdtdJ2LQjK2UV7r20+T3yeTZtaobBp5WXT2xAE9wh2BNGiRYtWXtXF8uXF16u8fUH5ZSqttNLK21AoBKWVr8bFxEShUIgPi1ZafJ6hlbNd25wFwUC8vgdt16K9cyhM7M6C8rL4sh600uKsMFAICoUdwfS6LlpQ2BAEwe5577T4PPF5hhaUQ2lDG9pF27V4fQMTwYOzcvaN9uBNuIjlSypPBUF8/crbFC2eFy0+T3xZ0aJFixZvQxBn5c27+JOpDQTlZfHT2LWh7c6G9ojCBQOlxevatKFdnQWFC+6wawOFeFn5su61XRva1DZseERwh+AO0+sLgg0D7zFx0S7a1HZtaLvX9ahdUNix43vtXpvag7ZrF6/rgolHbWgPKFy0TXvwplz8SVCWn9pEIVqhsGnxsviySiuttHKIp6LF6xpaeV5ppZVWWnnbhhattGjD6yqUs0KhvHkX/zXeYdeCiU2LFi3a0OLzbNpVu9eiXRHcozDxHQYe8a1D+flt2tQu2lUbGPgL/Ff4bzAxMXHvdV21oQ3tov0N/in+e9xjR7Brw+sqZ3faf45/jP9Be9BKu9Omz1M+T2HDFTv+Ff5b/B8Y+F6LVtrwNlzxDv8brggGLpiYWmm7NrwJF/873mHXgonNWbRoQ4vPs2lX7V6L9ojgGy34Dd4hflh8WZs2tYt21YKBbzBxh4FCMLwsvqyrs6FdtA2FHY/YUbhqm9dVCEobCHbsuGhTK+2iTZ+nfJ4NhYGJgd/hb3093uHPcYeh3aEwtdJ2bXgTLv4omFoQBMHQ4mxq5fN8r5X2B23Ton2P4IIdQeHf+7BCfFlxVtpVuyD4NR7wB+0OOwrleYX4sjbtUbtoV+0Rf8DfYNMKG4LpdU2ttCD4h3jE39M27aqVFp+nfJ4rJu4x8Gt851BaaVMrLV7fhnvcoxCUNrWL9qi98yZc/EkhzsrrC0orBBM7hpcF5edVWmmFaAOFiYlo0cqhEMSXFS1anBXKWaFQmF5Xeaq08vYNxPMKpZWz0uL1DUxMbCiH4Wxow5tw8UeFOCsE0eKstOnz3GnRokUbKOxaobChsHvZ9GWVs6mVtqEwMHDBxAXB1MpZafFl7Vq0qZW2YyIIBgq7Nr2ui7ZrmxZMxNmuvdPidQUXbFoQLYg2nMXbUFpQuGoDwebsnTflIgiiBRNBHOJ58XmiRYuzQjARDMShPBWH8mWVlxUGCkEQRIvnRYsva2pTK62cFQrlEG9DfL0mCkEQDF+Pwh3uMTAdBqKVN+niT0oLCkGwaXFWWvk8F+1RG9p0tmmFgSsGyiFaOZQvK1ppQyvtPQoPeMCGDTuCclbOype1aZsWZxsG7rAhKDxow+t61IZW2sCG0q7acBafJz7PnbYjmNg9NZ1FG17XxBXvEVy0IJjaRdu1aMOruvijaNHiEC2+jGhxFm2iUFohWjAcSou3o1AoZ8HEBXGI1xEtnjdRCDbLT2Ui2sBA+XjxugqFwkBp01fhIthwxY6BQlCIs2ilxeeJFmc7Cht23GPgATs27JieikN5Xmnx09i0R23TJgbeY8MjLigMxKcrHy9eVtrUytlAcK9dsSPYEJTnFSbKIQ7lqfg0E4WpBRNBMBGUdo+JRwxEK62cTc8rbaIQFApxVlpQ2sTQNgQPmHjEnXbVopUWLc7K84LSgtLi8xV2DAQ7hrOJOAytUJ6anjecTWeFwtTiRReFiempieGHBYWgvKycDW0gKGdB4YJHLXjEBfHUcIizaPG88uMF0aLt2jtMlFY+rDwvWhzirPw0SitcEFwxMPCIR9w7i0MwUZ4qBNHKj1O44BE7vsEFhYGJQqEQh+nzDS2IQ7RyKBQmgolNe8B3KIfC0HYfp7TSpkP8sHIWP+yKCyYGNlwxEC1aOYtWzoYWLVq00spZEMRHuSgEAwNBMBAMlKdKi5fFy6ZDIdqdsx2PGAgGLnh0Vs7K2fDxCvGyXYt2p23aO7zH0IIrBjZctTgrbTorZ8OHBfGyTQuCoZX2PR7xgB1Du8e3+K2XDZSnopVDeSpetiG4R+EeD3jEjk0LgkLhHhNTK62c7Vq0clba5lAoFK4YiLZhw0Thiis2PGh32oOn4mXT54mPU1pQ2LHhAXe481ScBROFQhAfFpQ2tCsKpW3a0KazcnKxY6BQ2DExtCCYzoY2nZWPE4dCOSvtgit2BMHEholyCOIsXrYhnheUszjbUZjOymFg4A53uGJgIg5xiFZeFof4OHEoRCuHaBcUBu5QeETwB5RWnhfEIVq04RBPlaeCQrDhEQOFRxTe4Q6lBdEKA0F8nnKIFgSFcgiCYEdhaN9gYmDHox+ntHhZPK8QP6y0DRMXTNw7TJTnFQoTcRjO4iza1DZnhSDa8KKLgUIcCgPTYWhBUNrwsnheaVcEU9u0oT0g2PANCncoPGI4K2flLM6ml01PlcM9gqFdEYeJbxBM/AGFgQfc+3RxGF42HcqHlXZxtmNHsOMBhe+wo7R43nAIyln5sKA8VVohuMeOB1ww8B6/R2mbFkw8YOJOK88rZ4VyCAqFQjwvCKZWGLjiDleHe21gOiuttGjR4uOUFmfxcaY2ccEDNm1HYXhqIg4DE3EoLVqhtOmsHEorHxYnF4XCFbs2EOwY2tSiTa0c4qmpledNLQ6FoQUT0a4IdgzEoRBncShPxVk5K2flbHc2UQ4XXHHVNgQThR3lqdKiledNZ+UsnldaPK+0DQOFC3ZccdWms0Jpwe6sfJp42Y53iHbBIx5wj2Ag2sDEFcMPixaHOFy0oU0tiEMQBNGmVhiYuMcVA9NThUJpE0EhPl5QDuUsPt57FAo7Ni0oh8JwFgyH0grRymEgKB8WLShncXJxh++wIxgobWJoU4tDsGnR4hAMh2hxKGf32tR+h0Jh4Ff4+/gO32JziKc2T8WhtGhxtiEOcRZtaI8INq0w8Vf4S1xReERhOsQhWnletItDtGhBOcRTFwQ7gguCoT3iG/wVdu0R9xjYnRUK0QpxKC3apkUL4jAc4qmJYGib9g0m/i0u2LV7XDHxLf6tVp4XZ0Mb2qZtKFy0i7ahsGl32obCBVd8iyv+PS74DQpXbSAoZ3/mbNMGSosWFEorvNdKKy3a7qycXfAb/JmPU86i7dqOwkThisKO4OpQeMDA1N4juKKwa9Hi5OJ/xN/HhqlFK4epRYsWhyAOcQgmJoIgmAimdkGwI/gDgg1XbCj8K+y4IAiCIIh2RRBMTARBsCMIgokgCKY2EQQT0d4jKEy8x8RAMBB8i/8V7xA8INgRBMFEEAQ7gmBiIpjaI4JgIpgIgl0LJoIgCArBFRMDQSH4f/Bb/J/YsWNi0x4QTEQLJoIgCIJCEMSX87/gf8Z/iQuuCL7BVftz/COtPC9aaaUNFH6NgXe4x3e4x7fY8Bvc4R7v8B3ucY8LfoU7h1/jn+D/xo7fIZh4xI4rdkxcMHHFFTt2TARTC4JCobSBQmFgYKBQGChtoFAobcfAP8fE/4sdA7/De7zH3+IBv8cDfof3+B7BAx7wBzziqg0M7YKBe1xwwcAdNtxh4g6FO2xatDi5+C/wDxGHoBBMBLtDEBR2LZiIFkwUgmAiCCaCXRuYGJh41K6IFlww8Q8wMRFMTAQTQRBMTAQTwcREMLEjCIIdU5uYCIKJiSC44oLgETsmgkdMBBP/DsEjJoIgCIKJiYkdQTCxYyIIgmAimJiYmAgmgokgmJjYtR07gomBiQ07/jPsuEMwEBSCiYmJYGoTwUS0YCIIJiaCIAiiTUxEC4LgexTuMPE9Cjv+A/4lgomgMLW/wr/QyvOmVigUBu5xwV/iDt/hHn+Bd/h7uMdf4g5/jnf4C9zjO9zhW9zjV9jwK2z4T7FjwxUDj7jgATsecYcrJq4YuGo7SgsKhYGBwkChMLSBiYEdQyutUBi4IniPR/wKD/gt7vF/YcPExO/xgN/iAf8OO36HB/wWV3yP4Io77Lhodyjc4xHvMHHBjm8wcfFUOYShUF4WhyBafLx4WbR4Klq8rLTyYeVQKBQKhdIKpRXKoVAO5YfFj1MolFYoFMpPI15WKAwUyscrZ0EQxJcRh2jxVPnyopXnBRMTExNBEM8rhzgEQbT48oIdOyaiBROFQjlEi+eVw8DAhg0DGwY2T5VPchEE0QpBEATxvEIQBBPRgolCMBEEwUQwEExEC64IgolC8HtMBFMLJoKJYCIIgmAimJiIFgRBEARBMDERTAQTE6VNBBNxuGCiELxHMLQdQTARBEEwEQRBEEwtCIKJIAiCIAiCIAiCixbseI/CAyYumPgGQRA8IAiCiYmJaEEwMREEwUQQTEwEQRAEQTARLQiCzdnQCsGu3WtXDFxxxdBKKy3OCoWBgYGBiYkgDkFQKK0cBjZccMEjJu5Q2BBM7JjYMTARbccVhWBgYGhDC0obKBQeUSgMbSAYmAgKpRVK27QgKJQWBMHExBVXPOIRG4JCobRCMDC0gaAcNmzawNQm4lBaaYUw/El5XqFQWqFQDuVQKIcgPk60IFocgjiUH6dQzsqhfFg5i7No5RAtzuLTBHGIL6sQxKEc4odFKxQKhUI5lFYoTxXKxwmiBUEQP71C+bBo5RBEKxQKhUKhnJVWWmnR4suK520YKAwUyqG0QnleOSuHIAiCIAjirFAO8WHxdy4KhUJQDkFQCMoh2nAoxFPB1ApBYWJHsGOiEGza1DYMLfhbBO+00korZ4WBqRUGgkKhHAqlFUoLCoVCtCsmHhFsmNixoxAEwcR0iEMcSgsKpRUKE/G8QlCIQxDtvcPusCEoFAaCq1YoxFkhCIJoQRBMxKEQH68Qh2Ai2BCHHYWpPTobnldaaUMrFAY23OEOd7jDwEChMBFcMfCoPaDwa1y0oRWueEThig07JoJCOQt27Ah2h4nSgkI5FApxViittKEVHrFjYsfE7rBhwwUTd5i4w8SOK3bsmJgOA0MbmBiYKAwMBAMDhYENm7PS4u8M/3/lrBzK88rzyk+j/HTi48RPI4gfVj4snooWX05pQfx4pZUWBMF0Vg7leeWHxaG8rvKyaKUF8XnKzyeI5xXi05RPF8+LVj7oYseOoQXTU1MLJuIQTMRhYmrBRBwmJoLggomJYGjR3mPiTvs1gu8RBEEQBEEQBEEQBFMLJoIgCII4RAuCYMNAtDttonDBRBBcEWza1EoLSisU4lAoBKUVCqUVBiZKK5RWKO1OmyhMRJt4RPCI4IqpFQaCQiEoBEEQBOUQBIWgEARBMDARBNGCQjC1HcHExBWFTbs6e4dv8XtnpUWbKE9N7NgxcMVAEBQKhcIFF9zhDu9wjwcUvkVhYKIwMBBsWjC1iXcoh6EF5TBQKAwUChsKhYGJgWBgYiAorbTCpm0ICsNZEEzs2HHFFRdtwwUbgg2lDUxtxxU7Bh4RRJsY2DGwoxBtaru/c/FDgiBaEEQrPyx+GuXDCvG8+GGFeKoQrRCUVj5OED8sniqtUAgKpcXPo7RC+fEKpRXix4sWxKcJ4ssrT5UPi69DaYXSogXB9OnirFAoFAqFQvlsF4WBwtRKCwpDC4LCRCFaOQSFgWBgakG0oQVBHKa2YWrBRDARFAoThdIGplbawK4VSiuttEKhEGeF6bAjKBQK0QZ2rTAxMBGHoDCw+2HR4qwQLVohzoI4TATRgomgtCAYWrSJIAiiBUEQBEEcgiAIgiAIgiAIogXRopW2YWAgWjCcXXHFcFba1AYKpQ0UChsmJkoLgiBatEIQTOwYDhOFaEMLJiYuKEwED7hiw8QFhUJQWrQgDsHAxEQhmCgEE/HUQDARZwMDA8Nhw4YLdi244oodV+xasDtccNEGBgauCDYMBANTK2ebvzP8UXlZnEWL1xWvqzxVlj+Jn0eh/DIUSotfpvLx4tOVj3ZRXk+8vmiFIIhDobRCUCjEy8qXUYgfJ/9fe3C2LFl2ZQV0zH08spFQYYXxwmfyVzzzxkeByWSlKmVcP3tiaIEdvO7NaLKJbORjeFS/bUGNoEaM+rBtxKhH9WEHDiwsBHEpio2NEyc2Nopie23jMBYWFhaCjWAhCDa2sRGPFoIguKNGsFCXIDiwUCxj4W4EcdnYuOOOO+54wQve4z0ObNxQHCiWcUdRFEWxsfE1vkLwDsENJ4Iay6gHy9PbirjEiEfxaerpSwji4+LpS4hHMeKHqUdBED/Izf8vqKf/pz5PEB8X1O9XUV9WUJegfrj6NO9R3BGcOLGxXYJgYeHAgSBe29hYKIqiKIoiHhVFsRGXuATBgYUiWFjYCIJ4LQg2NjY2NjaKIgiChQMHbrjhBS+4446NjW3E24Lgb9h4QbGMEws14k3L088nfjn1qF6rSz2K3774bSqC+G2KL6dGUZ/t5omiqBHfrx7FDxc/jxr1tvo08dtRP8zhw4ogxoHgwIF3eIcbbrjhhhtuOHDgwIGFhSA4cOBAEcRYKIqNjY2NYuOG5RIEQRGXIIjL9mgbG8FGjIViGQvvsHA3Dmwc2DhwIAiChSAIgoWFm7GNEwc2lrEQBDccuOGGAzcUN2wjxunB8o8uHsWlfpj6uPhhYgTxqJ4+R/zygrjUJZ4+V1EU9VE3QVHEZeGOhaJGUcSot9WlKDYWThzYqEtQl+LAHUFRBMXCxnYJinoUI0ZRIwiCugQ16tHGwkZQFEVQbAQbC9sI7rjh7lKPguBEUATFwgti1CUeBUUQFEERI0YRl3pUFMX2Wr0WFEFQI0Y9qrFw+rBgIyiCYhsLL1jY2DhQYxtBXWrUOIwTwcaJelRsnDhx4sCJO14QvCB4MRaKhRMLdTlQLNxQbJw4EZxYHi2XoEaNGMFCsFwWgmIZy1i4Y+PEHRtFsVEUcSmKYmNjY+PEiRPFHTWCjdOo14qiKLaxjBjxdzdFEJeiiBGjCGIUxUZRxAhqLARBUGzccbgUQVzu2DiwPNqoS1AEMTaKGkURLGxvixFsLBQxgvp+cTkQFEWNE3EJgrpsFEEQLJeNoh7F2C5FPdooio2i2CgWthEsY2NhexTUZRsbxUZRoyiKokaxEY9qxCgWDvwbbsYNd2yX4EBQBPVaUY9qFHHZKGoECwduCIIgCBYWgiCIEcRlYSGIURTFQhDExxVFEW8LgqAuNWpsBEEQY2Nj444TG0WNYCGI7xfEWKhHQVyKoqiPujlRYxsbRVCjKIpg48TyqEaNYqPYCIrgwGkUGxvFRnHDNooaQV2KoiiKYqGIsbFRbBQbRVEURVHcjWKjCDY2iqIoiqIoahQ1gqAoiqIe3RDU2KhRxFiosYwaxUIRLMSlKIKNjRhFsV2KoogRxAgWiqAItlFso1hGjWJjoyiKoiiKbWzc8Q1e8Df8EdsIagRFvS0IinoUBEEQrxVFUSzEJUZRBMFCsVAUxcbGiRPFRrERFEVRbCyjiEsQBMEdQVAERRAsxFgoFhaC77CNIFhYWAhiFBsnTpxYWAiCGqdxNxZObBQLCyeCeC1GsYyFIP7uJkYQxNhYuKPY2IixUSOoS1EUh3EiCBaCYKHGMuqyjaIoggNBUQTb2Ijxgo2FIF4LiqAIgiJYxoliI6iPW0ZRowiKhWIjCOpSFBvFdlk4UWyj2Ngogo1i48RGUSyXuhTba3UJNooaNYq6FEWxURTbKDZqnNhYvt/CiQMnNoL/gBeXoKixESxsI0aMGjWCIAiK7bV4FATBwoEbbrjhwEIQLBTFgSII4lEQLCO+XxAjRhAEQRAsBEFdtrGNoEYQj4KiKOoSBAc2DmwsFAeKA4cRlxoLcdnYOHEiXivi726Cu1HEOBEsBCeCZcTYxkJRY6EIDgRFsFFsbKOoS1DjQI07Noq7URQbRVEUB+JSxIhLUAQ1YsTYCGoUC8VGECxsl2BjGadRj4qi2CgWguAwim0EQTyKS7BQLASnsY2iRl3uqFEUdVnYWNhYqMvdoyAuB4qiRl1O1CiKoii+wolv8a8Igu/wDifiUbBQvPO2uhQxllEUJ+64Y+EFwQuCFxQvWDhx4sTC33BgIzhwIjixsHHHC4r3KF7wFU5svKC4o9hYRhAjKIKbESwEC0WwEJxYKJYRLBTv8YL3eI/3eMGJO+6444477rjjBS+4446NE9vYOBFsBAvBge9wosYNNyx8hdNYRo27v7v57/hnFEGMjQMnFk4UB4rihoV6rSiKoigO3PGVEaNGUdSosVGcxg3bpahR1DixsVHjHQ5sBEVRbBTb2CiCO77FiYXiRFEUG0VRFEXxNW6IsXEaxYGiqHFiYeFrbBR3Y2GjRrFRFBvBDTWC0zhRl6IoijtuuKHYLsWJYqNGURTfGkVRoy41iqJGcHOpS1FsvMfXRvAef8R/w1+MItgoir/g7lGNGkFdlnHDwl9xw59x4H9h4U+44T/hwJ9w4E848Acs/AHB18YNRVAUd7zDHQsnghP/ETVOLGwsFAeKGDW2sRAjRlDjQHAiqEtcvsOJf8Udf8WJP+M9/ow7/ow7/or3+Bec+Bec+A4nXhC8x0JxYOM9ihve4x2+ww3BO9xxwzaWUQ9u/iuCIiiCIsbCNhY2gm9xGEW8FqPYOBD8E77CO6/VJVjYCIKv8E84UKNGjLrUCL7Gn/A1isOoUaNGEQTFf0awEBwuRV2KAxsLB244EBTvfL+gKL7GP2Mh2FgoirrU5Rt8jT/iMBZOLB9WvMMf8I2xEQTFYdSoS/FffFyNhaJYeIc/oS4ximIZRYw7DvwP/AUbxUKN4i/YKOJRfdhC8D89WkYR1KhHMWrEiLH9MmLE2H6cAxvLOD1aKOpRjODEwkZxxzYW3uPEd1jGxsL2QTfLqNdqbNQoaiz8wSjitY3D2IjxLb7B9mHFgY0YCwvLpYhHxTKKYuHAgROnEY+C4ERQFDcsIziNuMQIio2NbWwsFMuo14qg2Ci2ccfNqBHEJfgrim+MGicO1GtBje9w4J1RBMHGNmrEoxej3rZwIljY2Dhc4rUgRhEUwc3YKOrRgROnzxcfFqNGfZr6PEH99GrUT2MbQb1W1GtBsF3ibXWJEZ/kZrvUqLcF2yhuLvG2haKIy2HE9ysW6hIUh0fxWlAjCF5w4DTi+xVBjLgURbxWjxaKA8Uygvq4hSJGcCAo4lKX4oZgGcHGYcTbYiy8Q11qBPWoHm0ftlEEpxEUQX1YvFYEC3XZxum1+jQ16m2nS32/elt9mvrh4lJfxol6rd62PTq9bXt0utRHLT+3elt9XDwV9fsWT19SUV9W/Srd/Fj1ZcQvI349ivhtic8Tn6eefqj6+dWv2vIl1agvq3776pcVr9WPFyOISxBPTz+r5XPFP56iflrx21U/rfjp1NPTJ1t+q4r6dPXTCeIfQxBPT79Ly5dQl/pti6f/o35d6unpsy1fSv1w8fsRT/9ePT39IpZPUaOIEZ8uiBGfr36cGDG2T1cEMepSn68o6tMFMeqHicvyeYq4BPXTKGIUMbbPE6/V09MHLU9PT0//oJan3796enp6w/L06xZPT08/k+XpH0dRT09P/9fy9NtVn66enp7+neXp962enp6+x/L06xdPT08/g+VLKOoSBPFpYgQbB+rzFUFQxAiCIAhiBDWCYntbEI9iBDGCoAiCeFuwEeNAfbqFYqEIivo08WFBvBbEJQiCIIgRbARFfL661Fienj7o5regLnEJYtSjuNSjeFTfLyjibfVaXYoaRY0YNeptG0VR1OcpahRFUWP5cba31aOiLjUOI4gRT09fxM2PVR8Wr9WojyvitSAu8f3iEiMIinhbEZcYMYIacYm3LSxjIS71YcHCQo34uGJhYWGhKILThwVFsV2K4PBhy6MaNTaCGkVw4vTDxNPTJ7n5uRVxqVGfr0YQxKhPEyMuhw8rghoxgmK5xNuK4MDNWCjqUm8rDhy4oUZQH3fDDcUyiuCdD1v4Ft/gdCkWTpe6xDg8ikcLQbFQ445vfJoiXounpw/63wTdhS07SlnhAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAAUAAAAC0CAYAAADl5PURAAAhNElEQVR4AezBy4pl25oQ4O8fc66IyMxzrcJDFSVogSCICII9e76B+Cp2fBMbYtOWL2DDTkF17HhpCWohlqIcL3U5e+/MWGvO8WvVj2eeWSsi8rYzV8RZ4/uCf5n8LhrSufRlhbIqB2W1STQkZhzx17wsfxv/Cf9BOWHF0WUt9g5KU/4r/if+gb2DsrisGWmzKH9f+cf2vlO6cnBZicABiX+K/4x/4mX5OToSiSMCizIrR2X2HMz8Ln7X49KXFcqqTEoq4dwJP1TeeFr4smZlUWZlUe6V38I3+JmyoOPosk72DkpTvsERP0BiQuJGWV3WpKRyr/xY+aG9SenKweUFZuWHeIODclAWe7OyuIxUTsodFiRSCTRltjd7DmYSibBJhOcnEVixIPE/PC/NXkPiO3yDewQSHa9c1g887KSs6PgOifQ8hb1UbpWm3CnheXiHwIzEhGazKKuHpctI5UdY8EMkQkkllaZ0pSnpkmYS6WHp8lIJm0AgPX8NiUAgEEogPE+hrFjRPW9prytdaUpXJiVdVleakkh0hE3aSyVdRtp0pWNSQknP2exMIF1et0lMSAQSK9Lz0+2dkEjlgFBWNM9TKoHwsoRNszfZC5d1pzRlxmzTlW4vlO5yEr9A4h1W3CGURLMX9sIlzc4k0vMRHhZIL0cgEDbN8CUkEoH0/KVNoqMjPC6VdFlpL5VE2kxKV8JzMJNIBBLheepIpWNBd3k3ymKvK4FAIOwFustalVQmJZUDZhyUFYkDEqvLSgSaksqkdGVSTkpTwmU1D0uklyORCAQmJe2FEp6DmUAogVAC6fK6EvYS6fkLpMeFywp7oYSSzgUCgdXlpV8fgVAaupchkUgvyUzaSyV9HelpoSS6kggE0vPQPWxVFpywIJSO5rJmJZVQVqUjsSKRyr3nZbEJdCSavYPnbUVX0uMmpbusGR236Jjthb2wFy6pGYZh+CzppZq9GM1eR0dzWUcfZsKE2aYhPA9hr9nrXo5U0vPXleZhTeke1lxWItHRkUogkUpTTvaaS2qGYRiu1OzZ6MqkNKV7WFO6521CYsZsL1zeaq8pXWlomJBIJJqSLisRNoFAoKErk7Iq4XlqaGgIpIetSndZN8qMjrAJ5w5KeA6aYXjRwuPSMDxldnHd05qSSkPzcqzKEUccEUh0HFxWsxdKs5dIpNK9DM1eKM3zFAg0BFLp9rrnYcWKEzpSCSSaEh6WLqkZhmG4UjOBVAKB9PU0ZVJWpSmhrOiY0bAivTwzAoE0fF/CJrGgY1JWZVK60jwvK1YsCITnbVUO6AilKal0pSnpOWiGYRiuVDMMw3ClmmEYhivVDMMwfK/SS9EMwzBcqWYYhuFKNcMwDFeqGYZhuFLNMAzDlWqGYRiuVDMMw3ClmmEYhivVDMMwXKlmGIbhSjXDMAxXqhmGYbhSzTAMw5VqhmEYrlQzDMNwpZphGIYr1QzDMFypZhiG4Uo1wzAMV6oZhmG4Us0wDMOVaoZhGK5UMwzDcKWaYRiGK9UMwzBcqWYYhuFKNcMwDFeqGYZhuFLNMAzDlWqGYRiuVDMMw3ClmmEYhivVDMMwXKlmGIbhSjXDMAxXqhmGYbhSzTAMw5VqhmEYrlQzDMNwpZphGIYr1QzDMFypZhiG4Uo1wzAMV6oZhmG4Us0wDMOVaoZhGK5UMwzDcKWaYRiGK9UMwzBcqWYYhuFKNcMwDFeqGYZhuFLNMAzDlWqGYRiuVDMMw3ClmmEYhivVDMMwXKlmGIbhSjXDMAxXqhmGYbhSzTAMw5VqhmEYrlQzDMNwpZphGIYr1QzDMFypZhiG4Uo1wzAMV6oZhmG4Us0wDMOVaoZhGK5UMwzDcKWaYRiGK9UMwzBcqWYYhuFKNcMwDFdqNly5UNJeVzq6kobh10kzDMNwpZphGIYr1QzDMFypZhiG4Uo1wzAMV6oZhmG4Us0wDMOVaobhg4Vh+HXSDMMwXKlmGIbhSjXDMAxXqhmGYbhSzTAMw5VqhmEYrlQzDMNwpWbuMCERNuHrWJXJ0wIH5RbpebhRjsqtclK6csQRTblXbnye8HlWZVJSOSgL7nFA4uj5CZtA4DW6c2+VV56nhqYk0l5TuufhFRYEGsJeKKGkEp6D2fCVBMK58LR0WamklyORhq8lvVQz91ixIhBIm6Z0X0az15WuHJRv0fAKJ9wicXRZR3v39m4RaGg2ByVd1r1yq0z2bnCDxV5DIj0PqaSyIG0m5cbz1BE2B+WkhNKUULrLequcsGJB4qDMyqy8U26V7pJmw1eQXq7w/KVff6GEvXBZaRNKeClmv3RAeNxkL5XweVI5KbMSykn5kU3Dvefl1t5JuVcawiaQ6J6WntZ8njsPW2xCSc9PKOlhqYTSPE8rAh0dYa8r3fM0KT/wtK40pbmkmTRcQiKRhi8hlVRCSSUM36dEemlm3uIt7pRZWe01e4sy+zyhzErYOymBwBEnBNLlvVLeKk3pSkPgBrdYlFnpnha+rLD3TrlV3uItQpmQCHSEy+r2GjpSuVdulWZvdVmT0hFoaJiVo71J6Uq6rFscbd4pBwSaclTe2EuXNPulRBi+bx2BVAJh0zwvoYRNOpdKet5CCQ8LL0uz111WIOwFAuG5m/ltZbYXSirhyzgpTenKQXmtHBF4hVf47zjhZBO+vqNyo3yrvFEOaPhX+Dv4bXQ0dLz2PByVV8qfKn8Tv4cf4Qb/y164rElJm4Y/wt/FnbLaSyVc1jeY8Er5W/jnSDT8ROnKrLxVust6jZ/iLRZMaHiHjlReKf9H6crBJc38N9zijXJAx2JvVkK5V2afZ1Ga0pUb5YDEd8otfow/RqJ7WvqyjspJ+dZeQ8N36Dig4YDEraelr2NSXiknZVJWnJxLl7V62IrVZvI83SBsGr7FWy9Dxy1+4MO88pzM/CZu8FNlVjoSq3Jj76jMPk+zl0rYe4dE4A5/XekeF0hfVlNWpSuT0pQ/wG/gfysrVqTHBdLXEUra+7HyrectlBmJQCCVRTnYS5c1o9tMnnarHD0PJ6xYMGNBYkHgTjkqN8qiNJc0+6VA2gvPSyB9uET4OkIJJZxrCHR0pKelryc9LLw8oYTnryF9uFBCSZfV0NBtAmGv2WtKc0kzK7oSNoH0fuHzdOWt8trePQJ3yqr8MQI/dlnvlFmZ7S3omDHjhI70tFDSlzUpTTkpoRyUW0x4i4ZJWVxWV1JZlY4V3cNOSnNZgbBpaB53r6TSXFbHd1iRmJXZ3qLM9rpLmkkk0l4ikR6WSvo8qaSSStikTcfk+QglPCzQEEgk0vulr6N7WNoLBMImDZ+rI2wSzYdLl3fADcLj0l56DmYCgbDXkJg9bFaa78et0uzNSioLDviJc+lc+LJu7Z2USZmUFSu60pBIe+EyViWUVJryzl5X0mXNyqJ0ZUW3OdhbldllrUibRMeMQFdW5UY5KunyTggllVBSuVNWZVLSJc3+XCrpXHpY+n6lh3UEQgmPC89X2EskAmmTLisNX1NHYlYaAunlCIQSSnoJZhITGlabRPjy3iqvlJNyUBo6Tmjo3i9twpe1KpNyUP5U+ZFywCubVNLHCx8ufZo3yltlVhYlMWH1uEAibNImnEsfJxF4rXynvMM7TMqizOi4U9Je97RmL5xLm/C0hqaccMI7dHsH5d5eQyI9LRDoPk4oDYGGRFdWvMZ3eKN0NGVVEmnTbQJNCSWV5nGJUFIJH2Mm0NGd6768Zi/sJQITTj5MeFx6Wvh0iVDS3q0PFx6WSvryQgklsdpbEfbSJpX0sPT5AjM6ViUQaDaBQNpLe2kvfJxUUgkPSwRCOeGAsAmEEh6WPlz6dKGkvcCCGYlAQ0dDKl0Je6kkAqGEh6WHhZL2wlNmOhasaDYdiQWB2d6qzJ6WnnawN3tYYMZJOeIGq73wtLCX9tImkJ422YTNj5XEPb7DO8zK4nHp65uRWJXvlO+U7lw4F/YC4XFpE86lpy1Y7CW6vckmbJq95tMsSkcgEDihodlLZUJisjlgxgmhzEoos9KVrqS92V5XunJQutKVVFJZPC5xwi1WTGjK7OOkvbBJhL0VgVC6MindU2ZWNATCuUQ6l0q3Fz5M2oTHzViVRCoTOsImkfbS0xrSwxJhLz2t20s0NBxwwOL5CKQSNqnMSsOEFauSaEp4WCJt0l7YpHPhXCKQHneLg68nkEoiEQibVBIdgVlpSsOKkxJKKKGEh4W9tJf2QgklkD7chI4ZHZPPEx4Xzk32AolUmqfM3GFGQ7Np6Jg9bFLSXtpLTwt0zEoqi01iwoRZmXwdaS8Q9hKhNGVVfoE7hHKPwB2O6D5PeFp6WiqLvVRSmRA44AYLVnSfJ32/Ag0zAqvSlUAg0DF5WjoXNisC3blEQ9g0m6YETsoJgcCEVVmQSCWV1dO6Ekook3L0tAlp05SurLhDoikdgUA6l2g2iVQCgY5UAqGkvbAJJbB4WvNnZgKBFStulMTqcV1pNulcV8LD0rlAs+k2i720CefSJpxLe2EvPG1VJg+bsWBRJiQ6wudLX9akBGZlweJ5mnHCETdINJuGjhXN+6VzadOUpnQlkTaJRCKR6JiUho4bLJiwIpVUAoHmw6VziXAulFRSSSWVVCbcI5SO5mGhhL1A2GtIJWwCifCwRCKRCA9Lf2bmn+ENfojELQILuk1TQrlH4rWSStokmk0qabMqr5TFXkPHHSYs+Ble4xWONunc5FzahJJK2puQNmnvZO81Et8o79DxDX6Ov4fADxE42aRNKuFhqcw2qaSSCJt0bkaiIXFEIpXfwM/xl7AicMIJDc25QCqBdC6VSUklkTbNJp1rSMzKjfJT3OIPMOMb5YAFE17hP9oLe2kv7S3KCYF75aTcI3BUVmVBoGPBhAX/Fv/audXe6sOkkvZWD0tlUg7KnXKrTMpv4Me4U5q98OnCw8LjJh9j5h8aflUikUh0pSOR6EjlWyQSHXfoOCBxj8R3+Hf4F8oBicXLsGBFR8fJ8/af8Xv4fcz4BRK3WHDAT/A79sJeKqF0m8AtGhI3yo0y4YADJtxiwg0SM2YcsCqv8Tfwh1jxDokDTlixINDxDh2BBR0rEmnTkLhB4Eb5GQIzGt6g4RUCrxG4Q8MtArPyBg3/Bh3fYsUrfIsF91hwROKIBfc4IJUjOk4IpaGhY1ZuEJhxhwNmHLDioBwQSirhV838e/wV3CgTEisSHYmwSSwIHJVERyqJjkAi0ZFIdCROSqBjRcekJBKJxAEdf4SOWyQ6OhIdiUSioyPRkejoSHSsSCQSK7rS0ZFIdHQkDljwCyQCKw5InNBxQMc/QiLQMSORSCQ6OjpWJBIdKzoSiUSiI9HR0dGR6Eh0JBIdHU1ZseKIjhVdWfFDrHiHREPiHomOjo5EVzoSHakkOhKJjo5EIpFIpaMjlUQiEQgkOo4IBL7B7yPxc6TNAb+N/6I0D0sl7c2YcY8DTrjBPW5xjwkrDjghkJix4ICTcosJRyw2gcQrnHCHIwIn3GLBhAUHLEisaMoNAj9AwxsE/ioCv4mGGzT8Fhp+Bw1/GYGfIfAjNLx2rqPZHHGDBTNOaDjiFToCR9xgwQGpBBbMeIcDVtzgLW6RmJAIH6oRCE9Lm0Qq6cOlp6WSzqWSnhZKeFzYBAKBQCCUQCiBsAmETXi/9GkCgVACgUD4fqSnBQINgfDhwl4ikUhfRtqkks6F56Ojo3u/sEmbRCKV9HWk0u2lEvZSSQ8Lm1AmpWFC87DwMWa/tCgrupJIdHRlRWJFYEIilVRSCaSSSKSSeIPECYmGVBJHdNwjkej4CTp+gURHoiPRkUgkOjoSHR2pdKxIJDo6OhIdHYmOREfHO6xIJO7QMSHREfhTJH6CxJ8iEEglkfYSibSXzqWP962SWHFA4AYdCzr+CIkTErdINKS9RCCRNolEKolAIJBIJBKJRCCQSiKR+E4JdExKIvFH9m6Q+Cl+ij/0aRKJxAmJRVmVVTkpqSzKqjSbGxwRWBFIvFW+sfdOOSkdabMqRwS60hCYEZjRcEDDAQ0NzSYQaGhKR8OKhlQSgYMyKzfKa6VjxoTApIRNKKGkcofwfqmkEkjNL4WHBQKhBAJhEzaBsEmkD5NKIpW0SaRN+DSBsBc24XFhL+2lEjappL30cRJpk76sQCJtwia9XyqBQCAQCJtQAuFcIHyYRCqJ9GWFzxMIBAKBsBcuKz2uIdCU8HHCXnhcIpE+T/ozM29wZ5PoSqKhoysrEpOy2gTSwxJpE0ibZhNYlQmJGYkFiQmJWyQ6Eh2JjkQi0ZHoSHQkEh0dHYnEio5ER0eiI9HRcYOOGR0LEr9Axy0Sb5DoSPwMK/5E6WhIdDSbRFcCiVA6uk3aBDoCXUl0BBpeK4kVDakkAolE4g4diY5UAoGGbhMIdJtEINARSB8ukMobJI5oaEhMyrf2jsof4zW6T7Mila50exNW3OGIGyw4IHGLP8GKVZmwYrY3YcENTko6N2FxriNsGsImEGhoaGg2XVnR0G3C3orAghvc4xYLZhzRcI83mJTm/RoCs3KPW6yYsWDGEQcsStoLf6Z5UtiEh4WHhe9H+P6kD5O+H4n0fuFx6Vwq6csJJZE+XSihJBKJbi9swsPC+6X3S19H+Djp84WvKz0skL689LBUwmNmv9SVjo6mJDoWJdGRStgLJT0ukMq3SEzoaOhIZULDDTq+Q+ItEg2JjkRHoiORSHR0JBIdXelYkUh0dHQkEh0diY6OjrdI3KHjF8otVnR0JBIdiZ9jxa29dC6RSJv0tEB6v1RSuUdiRUdDIpSm3CORSHR0JDoSiUQi0dHRkUgl0ZFIJBKJjkQilURHYlYWJCZ0HBCY7S3KCd+h2Wv2FgTS3owZ73DAghvcoymJxAkd75RFeafM6AgEuk1XFpvEhNVmxoIbLFixelwgsCCwIm260pVE2nSPawg0BCZlUg4I5zoSCwIdgQNWzOhINGVGQyizMqOheUrzXolEIpFIJNKHSd+P8LjwuPR+4WFhE0oo4cMk0vulc4FAIJRAIHw9gUAgEAgfLxAINJ8nlUT6eOn5SS9D2ISSSirdx0t7gUAgEAjfl5kjOgIdgaakEpiQSAQ6AulcKoFEIJVEIhG4QSKVRNqs6OhINCRmJDoCaS+Q9gId6Vx6WCKRSKUjkbhDIpF4hURHoCsNHYmOO3R0JAINq/dLJe0FUkklkPYSqQQ6EokJXWnoNokFHQ2ppE0i0JFIJFLpaEgkGlJJJBKJVBKJVFIJLEjc2DQckQgkutJsbtHtdefSuQWhJMImlFBSaeg2B5yUQKLbNEw4IZDoSigzFizKW6RzicQ9Gk4IJZDo6EoqBwRu0DChYUJTOhpCSSWUriw4YEXgiBkdTZkRmOyF0nyY5kM0fy48Le2lki4rXVY4F4b/L30dgfDrIWzSr6fw4dKXNBMuJ11eKoFEIm0CoQQSgUB6WvgyAunTpL30sgVSCaQSSnpaU7pPtyj3yklZlbTX0G06ur1EYlVSWZTFwxpWzFjRlBmBN2j4AQIHNEwINDSbjsAJDfdouEfDHZrSlEmZlRvloEzKLRILDujOhb0FgRUHHJUVNzjigAUHhNKV5lc1wyMSYRNK2AsfJg1fQyC8XxheorQXCIRP0eyE4VeljxMI7xd+vSXS1xX2wpfXMeEOB7xGw61yUCZlsmloSnculUkJ5Ua5QWCx15VEoqOjo9skEiecsGLFihUd3WbChBvc4ICDp3VlUU7KCSveITAj0JRmk/YaGmblFSbcIHCLhglh09D8Rc3wBYXLSXvpXNqkvfDyhZcrvFzhpZgN/08ikUp4XNoLny58Gamkh6UPE16OdBmrsiodq7Iqq3JSunL04VLpyorEjMWmYbVJZUHghIYjAgc0TAhMaGhoNt3DVqyYsOAWK2abphyUg3KnNJuOxaZjxgmzTSiJsFkxYUVD2mt+VXP1wl7YpE+T3i98mlACYS8NHyMMv25SSaT3aQQS3V5DIpBIJZEIhMelTSLREehoCHthLzGhI5REINEQSJtQ0l4gEAglkQgEwl4glbTXEUiEkkiE0hHoCKQSWDEhbdJeINARSAQSDR2hpE3YCyQCgUQogUAglEAgkPYSiUR3Lp0LJRA2gXAulYb0tEAilFA6Eg0dTekIpBIIhE+3KiflqHQllFACgRs03OIGB0zKhAlpb1FWpaE5FwgEAg0NgcCMWUklbAJpE2hoaJgwYcKMCZMSSldSWZQViaO9CQ03uMEN7jDjFgdM9u7RcVJSaWhoaGho/qJGItAQCCURSiCUQCiJREcibcKmYcKEhkTHgkAglEAgEFjQlYaGUDrSJpRAIJBIdCQSiUDzuECgIdAQCCWQHhcIZUJDQ9isCAQCgbDXkUog0Gw6EmkvEEibRNrr6Eh0JBIdHc0m0NAQaM6FvY6Ojo6ORCKRSCQSqSRWBAKBsAmE0jBjQSqz0m0CE0IJJBLp+xNKKKGE0tAQCAQCgUDYNITHBQLhwyQS6XGBQNhLJBKJroQSSle6sippE2g+XChhLxA2iUQivc/MilS60pEIpJJIJAIdK5q9VFJJdCQ6AonAhFVJdHQkOhIzupJIJZA2iUQikUg0JELp6Eh0JDoSiUQikUgsSqIjEejoSCQSiUQikUglkUogkEgkEmlvRiCVjlQSoTSk0pRUEg2JQEPYJBKBjo5QEoluk0gkQgmEEmhIBBKBriS6kmhKKomOjkQikUgkEl3pWHCHE97hDboSSCWQSB+mKR2BVAITVgQSMxYccMSEBaGEkugIrFidOyGRSI+7t7cqK9K5CQ2hNAQa/m97cJQbR3qdAfTcv6pJyZMxMkBevEzvKs95y268AAexBcQWya7/C4SLoNDpFkVqNJrRqM4ZWrBhYKIwtScMPKHwFqUt2km701ZtsQuCBzzhhCf8gA2rS0HhPe6w4YSBwqqdUFhRdps2UZg+WCmtUChtYuCMYGKitIlohdgFQbBoGwqFgUJhINrQYje1IAgKCwpBUJjaRGlPmBgolGuFoBAUCkFhaBuCiUJ82tCCaEEhGAgmCoXYBcFEMO0GNgRTCyYmgsJEMLFhIgiGXeyC6VrsChNBtGhB7IIgmAiCqQUT0TZMDB83sGHBhonCv+DJrhBEmygMTM+bdrELppcpbWDDig0rzm4b2FBaIa4V4tMKhUKhUCgUCoXCwEAhWjARTEyUl4kWu0JhwcSCicJwrVyKNlyaWLBhuC3+z0rhrAWlbSgMFDYUhlba1AaCaANBYUEhKEwEE1MLYleItiDaGRPBWQuCiSAIggVlF5RWdoWgEK200iYK0YKBYKJQGJh2hYmhbVpcCoJgIhgoFBYtmFqhUC6VXWEgGChs2tSCaLE7I1oQxG5gYmBiIHZnlwpltyAIosVuQ7QgCILgDhve4h8oFB5wwoZyqTAQ3GlxqbQFsZsobcWCOwR32HCHM+5QWLHhHhP3CE74BxZMFAY23GPDCU/aEwqPuMMTzti0J6w4Y8HEtCvcoXBC4Ul7xMAjBh5QmCg8YWBiYKLwgOCEJyx4xB2ecMJ7vMUD7vCAgQf8KyYKhQ1nLHYLzlixYMWq/RN3mDjZLS5N7axNH6z8B35CUChtYsGGgQ3BgiBYMRDXgiAIgmDBGXdaadGCIFq0iWDTVky7IFoQbcPERLQTFkwUgiCYCKY2ERTOeIsNA8GGIAgmgiAIguAeK0qb2LRgQRBE2zAwcI+J4KwNTEQLJoJgorAiWmHTNsQuCILgjBUrgmkXbAgmogVB8FYLgmixixYE0QqrXeyCYOIR91rhET/g3/FOCwoTQfAOjz7fg/bg2/F37W/af6HwEwp/R+G/tXco/BWFv2o/ovBWu8OGO5zxFhtWPOIHnPEGZ6x41DacsOENNqwoTAw8YsF7PKKw4m844YwVTzjhESs27Uk7a/HByp9RCApBIShtYGoDE4W3WLSgXCstmFhQ+CPucHItdoWBiULhDn/EgmjRSotdtMI9fsQ9gkWLFi1aUCgE/4bCQGGxC2IXLJgYWLBiQSE4+bhCENzjJwwUJgaCIHaxe4N7/IBFG9gwPC844Q94o00UCsGiRYtd8CefFm0gCAZO+BGxKy0IhhaUdsaC/8Q7TAQD0YJ3KMTHDUQrTJcWFM5uK6206dIb7UGLVlphunavPbi0aAOFR7eV9hftL9q9Fu1Ri1baop1RKMQuLpU2UFgRLYhLZ7vYlTbsCpsWl0obLpUPVoYW16JNRAuiDfxBC8q1iUWbKO0t3mB6XrBgorSBgWEXlEvB0IJgYMGCDZtWLhUKGwpBsGJohU0ru9IKwcTE1CYGgqHFtaAQTARTO2PVohXKrvA/CN5o0TYsiGuFaA9YcNKCQmFiatHKpSctbhvYUBiYmFjsyrVCaUEhKKzaRBCXFmzYfFpcKi1eJl4mbptuK8THlY8rHxdtel7sgnheIVoQLYhWWlAobC6Va/F68cHKtIsWtxWmFqx25baBICi7RSsfFwzErhAsLpVrhWiFwhMWbFr5uKBQWtkFQbkWlwaCBcHQCvFpA0FphQWFoOxiF6woDK0wsWjlttIGTohdtEJcikvT8yaCwqYVgkI8r1wLCgOxm9rm5WIX1zYvE7e9d1s8773bNm3zvHcuRXvwvGiblyktCDZt83mm14m2uWX4xcVt8WnlEMTvWzl8TUF8XfFbtPrZ4usov47y2xGUb0t5nfI6cfhc8cuL37Lhq4oWX1d8++LXVa7Fz1daoewK5XD4JQ2vVr4/QXxZ5dsVX1b5cuJweKnhmxXEy8WXUyjfh0I5HH6Phq8idvFtK4cP4rclDofXGr6a+Hzl96Mc/r84HH4Nw4tEC0orL1corbxe/DyllTa9XFAoLXbxekEQL1coLT5P2Q2vE5RdIb6MoLSgtOl1yrU4HJ4zHA6Hw3dqOHwH4nA4XBsOv3HlcDj8MobDdySIw+HQhsM3LF4uDofDpeHwOxeHw+G24fANKIfD4csbvoogdoVCeZnSChML4vWCQiEorVAoFAqlFaIVgum2QrlUWqG0QiEoFMpthYnSFsTLDQQDQSGIlynPK5RrhbIrFAqFQmmFiUJQXi920YbD4Tmrb0Lsyq5QWlwqu7hULsXHFYJyW1yLXRAtiFZatLhtIgiCeJ0gWhAE0YafZ7otLgWxi7ZohdLK4fA1rH62eF65Fi0+LSjXCmVXPq7sSisUgnJbUHallVaIVnbltoGhDZRdPK8wMBCtfFowMDAwEASFzfMKQTDtgsLiecOlaNEmCtGCwobN5ymHw0usfnFB2UWL14tWKJQWL1Na2S2eFxSilVYIhl25LSgsWLWBIHZxW7BgwYpohfi0FSuCoQWFk+cNvMUbbHbBwGYXu9IWl8qlgUIwEO2MN14mKNfK4fCc/wWQn0TduldZ/gAAAABJRU5ErkJggg=="; -TestRegister.addTests([{ - name: "Split Colour Channels: Default (JPEG)", - input: testCard, - expectedOutput: testCardSplit, - recipeConfig: [ - { "op": "From Base64", - "args": ["A-Za-z0-9+/=", true] }, - { "op": "Split Colour Channels", - "args": [] }, - { "op": "To Base64", - "args": ["A-Za-z0-9+/="] } - ] -}]); +TestRegister.addTests([ + { + name: "Split Colour Channels: Default (JPEG)", + input: testCard, + expectedOutput: testCardSplit, + recipeConfig: [ + { + "op": "From Base64", + "args": ["A-Za-z0-9+/=", true] + }, + { + "op": "Split Colour Channels", + "args": [] + }, + { + "op": "To Base64", + "args": ["A-Za-z0-9+/="] + } + ] + } +]); From e6932401ad0da647d2b9176fc83450e2c535ed2e Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 26 Dec 2018 16:35:34 +0000 Subject: [PATCH 037/147] 8.18.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 85678c7a..68b9a43e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.17.2", + "version": "8.18.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index f4f6e25d..554f77c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.17.2", + "version": "8.18.0", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 5acee804637bd49acc6a73d916fc77f11f59c39f Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 26 Dec 2018 16:50:32 +0000 Subject: [PATCH 038/147] 'editableOption's are now full width. 'editableOptionShort' type added to replace the old style. --- src/core/Ingredient.mjs | 1 + src/core/operations/Split.mjs | 4 ++-- src/web/HTMLIngredient.mjs | 29 +++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/core/Ingredient.mjs b/src/core/Ingredient.mjs index dda66b8f..00dd5f6d 100755 --- a/src/core/Ingredient.mjs +++ b/src/core/Ingredient.mjs @@ -95,6 +95,7 @@ class Ingredient { case "binaryString": case "binaryShortString": case "editableOption": + case "editableOptionShort": return Utils.parseEscapedChars(data); case "byteArray": if (typeof data == "string") { diff --git a/src/core/operations/Split.mjs b/src/core/operations/Split.mjs index 88bf8aec..1340a383 100644 --- a/src/core/operations/Split.mjs +++ b/src/core/operations/Split.mjs @@ -26,12 +26,12 @@ class Split extends Operation { this.args = [ { "name": "Split delimiter", - "type": "editableOption", + "type": "editableOptionShort", "value": SPLIT_DELIM_OPTIONS }, { "name": "Join delimiter", - "type": "editableOption", + "type": "editableOptionShort", "value": JOIN_DELIM_OPTIONS } ]; diff --git a/src/web/HTMLIngredient.mjs b/src/web/HTMLIngredient.mjs index d026e1f3..bb01d7de 100755 --- a/src/web/HTMLIngredient.mjs +++ b/src/web/HTMLIngredient.mjs @@ -165,6 +165,35 @@ class HTMLIngredient { this.manager.addDynamicListener("#" + this.id, "change", this.populateOptionChange, this); break; case "editableOption": + html += `
+ + + ${this.hint ? "" + this.hint + "" : ""} +
+ + +
+
`; + + this.manager.addDynamicListener(".editable-option-menu a", "click", this.editableOptionClick, this); + break; + case "editableOptionShort": html += `
Date: Wed, 26 Dec 2018 16:50:36 +0000 Subject: [PATCH 039/147] 8.18.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 68b9a43e..a4c4355c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.18.0", + "version": "8.18.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 554f77c7..43ad47ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.18.0", + "version": "8.18.1", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From f2d115ee4da1485dc821312db2562b344662f83c Mon Sep 17 00:00:00 2001 From: Klaxon Date: Sat, 29 Dec 2018 00:44:59 +1000 Subject: [PATCH 040/147] add lorem ipsum generator --- src/core/config/Categories.json | 3 +- src/core/lib/LoremIpsum.mjs | 221 ++++++++++++++++++++ src/core/operations/LoremIpsumGenerator.mjs | 70 +++++++ 3 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 src/core/lib/LoremIpsum.mjs create mode 100644 src/core/operations/LoremIpsumGenerator.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 686c9842..3678a88b 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -375,7 +375,8 @@ "Parse QR Code", "Haversine distance", "Numberwang", - "XKCD Random Number" + "XKCD Random Number", + "Lorem Ipsum Generator" ] }, { diff --git a/src/core/lib/LoremIpsum.mjs b/src/core/lib/LoremIpsum.mjs new file mode 100644 index 00000000..9712a429 --- /dev/null +++ b/src/core/lib/LoremIpsum.mjs @@ -0,0 +1,221 @@ +/** + * Lorem Ipsum generator. + * + * @author Klaxon [klaxon@veyr.com] + * @copyright Crown Copyright 2016 + * @license Apache-2.0 + */ + + /** + * generate lorem ipsum paragraphs. + * + * @param {number} length + * @returns {string} + */ +export function GenerateParagraphs(length=3) { + const paragraphs = []; + while (paragraphs.length < length) { + const paragraphLength = getRandomLength(PARAGRAPH_LENGTH_MEAN, PARAGRAPH_LENGTH_STD_DEV); + const sentences = []; + while (sentences.length < paragraphLength) { + const sentenceLength = getRandomLength(SENTENCE_LENGTH_MEAN, SENTENCE_LENGTH_STD_DEV); + const sentence = getWords(sentenceLength); + sentences.push(formatSentence(sentence)); + } + paragraphs.push(formatParagraph(sentences)); + } + paragraphs[paragraphs.length-1] = paragraphs[paragraphs.length-1].slice(0, -2); + paragraphs[0] = replaceStart(paragraphs[0]); + return paragraphs.join(""); +} + +/** +* generate lorem ipsum sentences. +* +* @param {number} length +* @returns {string} +*/ +export function GenerateSentences(length=3) { + const sentences = []; + while (sentences.length < length) { + const sentenceLength = getRandomLength(SENTENCE_LENGTH_MEAN, SENTENCE_LENGTH_STD_DEV); + const sentence = getWords(sentenceLength); + sentences.push(formatSentence(sentence)); + } + const paragraphs = sentencesToParagraphs(sentences); + return paragraphs.join(""); +} + +/** +* generate lorem ipsum words. +* +* @param {number} length +* @returns {string} +*/ +export function GenerateWords(length=3) { + const words = getWords(length); + const sentences = wordsToSentences(words); + const paragraphs = sentencesToParagraphs(sentences); + return paragraphs.join(""); +} + + /** + * generate lorem ipsum bytes. + * + * @param {number} length + * @returns {string} + */ +export function GenerateBytes(length=3) { + const str = GenerateWords(length/3); + return str.slice(0, length); +} + +/** + * get array of randomly selected words from the lorem ipsum wordList. + * + * @param {number} length + * @returns {string[]} + * @private + */ +function getWords(length=3) { + const words = []; + let word; + let previousWord; + while (words.length < length){ + do { + word = wordList[Math.floor(Math.random() * wordList.length)]; + } + while (previousWord === word); + words.push(word); + previousWord = word; + } + return words; +} + +/** + * convert an array or words into an array of sentences" + * + * @param {string[]} words + * @returns {string[]} + * @private + */ +function wordsToSentences(words) { + const sentences = []; + while (words.length > 0) { + const sentenceLength = getRandomLength(SENTENCE_LENGTH_MEAN, SENTENCE_LENGTH_STD_DEV); + if (sentenceLength <= words.length) { + sentences.push(formatSentence(words.splice(0, sentenceLength))); + } else { + sentences.push(formatSentence(words.splice(0, words.length))); + } + } + return sentences; +} + +/** + * convert an array or sentences into an array of paragraphs" + * + * @param {string[]} sentences + * @returns {string[]} + * @private + */ +function sentencesToParagraphs(sentences) { + const paragraphs = []; + while (sentences.length > 0) { + const paragraphLength = getRandomLength(PARAGRAPH_LENGTH_MEAN, PARAGRAPH_LENGTH_STD_DEV); + paragraphs.push(formatParagraph(sentences.splice(0, paragraphLength))); + } + paragraphs[paragraphs.length-1] = paragraphs[paragraphs.length-1].slice(0, -1); + paragraphs[0] = replaceStart(paragraphs[0]); + return paragraphs; +} + +/** + * format an array of words into a sentence. + * + * @param {string[]} words + * @returns {string} + * @private + */ +function formatSentence(words) { + //0.35 chance of a comma being added randomly to the sentence. + if (Math.random() < PROBABILITY_OF_A_COMMA) { + const pos = Math.round(Math.random()*(words.length-1)); + words[pos] +=","; + } + let sentence = words.join(" "); + sentence = sentence.charAt(0).toUpperCase() + sentence.slice(1); + sentence += "."; + return sentence; +} + +/** + * format an array of sentences into a paragraph + * + * @param {string[]} sentences + * @returns {string} + * @private + */ +function formatParagraph(sentences) { + let paragraph = sentences.join(" "); + paragraph += "\n\n"; + return paragraph; +} + +/** + * get a random number based on a mean and standard deviation. + * + * @param {number} Mean + * @param {number} stdDev + * @returns {number} + * @private + */ +function getRandomLength(mean, stdDev) { + let length; + do { + length = Math.round((Math.random()*2-1)+(Math.random()*2-1)+(Math.random()*2-1)*stdDev+mean); + } + while (length <= 0); + return length; +} + +/** + * replace first 5 words with "Lorem ipsum dolor sit amet" + * + * @param {string[]} str + * @returns {string[]} + * @private + */ +function replaceStart(str) { + let words = str.split(" "); + if (words.length > 5) { + words.splice(0, 5, "Lorem", "ipsum", "dolor", "sit", "amet"); + return words.join(" "); + } else { + const lorem = ["Lorem", "ipsum", "dolor", "sit", "amet"]; + words = lorem.slice(0, words.length); + str = words.join(" "); + str += "."; + return str; + } +} + +const SENTENCE_LENGTH_MEAN = 15; +const SENTENCE_LENGTH_STD_DEV = 9; +const PARAGRAPH_LENGTH_MEAN = 5; +const PARAGRAPH_LENGTH_STD_DEV = 2; +const PROBABILITY_OF_A_COMMA = 0.35; + +const wordList = [ + "ad", "adipisicing", "aliqua", "aliquip", "amet", "anim", + "aute", "cillum", "commodo", "consectetur", "consequat", "culpa", + "cupidatat", "deserunt", "do", "dolor", "dolore", "duis", + "ea", "eiusmod", "elit", "enim", "esse", "est", + "et", "eu", "ex", "excepteur", "exercitation", "fugiat", + "id", "in", "incididunt", "ipsum", "irure", "labore", + "laboris", "laborum", "Lorem", "magna", "minim", "mollit", + "nisi", "non", "nostrud", "nulla", "occaecat", "officia", + "pariatur", "proident", "qui", "quis", "reprehenderit", "sint", + "sit", "sunt", "tempor", "ullamco", "ut", "velit", + "veniam", "voluptate", +]; diff --git a/src/core/operations/LoremIpsumGenerator.mjs b/src/core/operations/LoremIpsumGenerator.mjs new file mode 100644 index 00000000..228daaa1 --- /dev/null +++ b/src/core/operations/LoremIpsumGenerator.mjs @@ -0,0 +1,70 @@ +/** + * @author klaxon [klaxon@veyr.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import { GenerateParagraphs, GenerateSentences, GenerateWords, GenerateBytes } from "../lib/LoremIpsum"; + +/** + * Lorem Ipsum Generator operation + */ +class LoremIpsumGenerator extends Operation { + + /** + * LoremIpsumGenerator constructor + */ + constructor() { + super(); + + this.name = "Lorem Ipsum Generator"; + this.module = "Default"; + this.description = "Generate varying length lorem ipsum placeholder text."; + this.infoURL = "https://wikipedia.org/wiki/Lorem_ipsum"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Length", + "type": "number", + "value": "3" + }, + { + "name": "Length in", + "type": "option", + "value": ["Paragraphs", "Sentences", "Words", "Bytes"] + } + + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [length, lengthType] = args; + if (length < 1){ + throw new OperationError("Length must be greater than 0"); + } + switch (lengthType) { + case "Paragraphs": + return GenerateParagraphs(length); + case "Sentences": + return GenerateSentences(length); + case "Words": + return GenerateWords(length); + case "Bytes": + return GenerateBytes(length); + default: + throw new OperationError("invalid lengthType"); + + } + } + +} + +export default LoremIpsumGenerator; From c7e9115994ccb1f316c9bef55b9b703ace26182e Mon Sep 17 00:00:00 2001 From: n1474335 Date: Fri, 28 Dec 2018 21:49:40 +0000 Subject: [PATCH 041/147] Restructured tests directory --- Gruntfile.js | 19 +-- src/core/config/scripts/newOperation.mjs | 2 +- {test => tests/operations}/TestRegister.mjs | 2 +- {test => tests/operations}/index.mjs | 118 +++++++++--------- .../operations/tests}/BCD.mjs | 2 +- .../operations/tests}/BSON.mjs | 2 +- .../operations/tests}/Base58.mjs | 2 +- .../operations/tests}/Base62.mjs | 2 +- .../operations/tests}/Base64.mjs | 2 +- .../operations/tests}/BitwiseOp.mjs | 2 +- .../operations/tests}/ByteRepr.mjs | 2 +- .../operations/tests}/CSV.mjs | 2 +- .../operations/tests}/CartesianProduct.mjs | 2 +- .../operations/tests}/CharEnc.mjs | 2 +- .../operations/tests}/Checksum.mjs | 2 +- .../operations/tests}/Ciphers.mjs | 2 +- .../operations/tests}/Code.mjs | 2 +- .../operations/tests}/Comment.mjs | 2 +- .../operations/tests}/Compress.mjs | 2 +- .../operations/tests}/ConditionalJump.mjs | 2 +- .../operations/tests}/Crypt.mjs | 2 +- .../operations/tests}/DateTime.mjs | 2 +- .../tests}/ExtractEmailAddresses.mjs | 2 +- .../operations/tests}/Fork.mjs | 2 +- .../operations/tests}/FromDecimal.mjs | 2 +- .../operations/tests}/FromGeohash.mjs | 2 +- .../operations/tests}/Hash.mjs | 2 +- .../operations/tests}/HaversineDistance.mjs | 2 +- .../operations/tests}/Hexdump.mjs | 2 +- .../operations/tests}/Image.mjs | 2 +- .../operations/tests}/JSONBeautify.mjs | 2 +- .../operations/tests}/JSONMinify.mjs | 2 +- .../operations/tests}/JWTDecode.mjs | 2 +- .../operations/tests}/JWTSign.mjs | 2 +- .../operations/tests}/JWTVerify.mjs | 2 +- .../operations/tests}/Jump.mjs | 2 +- .../operations/tests}/MS.mjs | 2 +- .../operations/tests}/Magic.mjs | 2 +- .../operations/tests}/Media.mjs | 2 +- .../operations/tests}/MorseCode.mjs | 2 +- .../operations/tests}/NetBIOS.mjs | 2 +- .../operations/tests}/OTP.mjs | 2 +- .../operations/tests}/PGP.mjs | 2 +- .../operations/tests}/PHP.mjs | 2 +- .../operations/tests}/ParseIPRange.mjs | 2 +- .../operations/tests}/ParseQRCode.mjs | 2 +- .../operations/tests}/ParseTLV.mjs | 2 +- .../operations/tests}/PowerSet.mjs | 2 +- .../operations/tests}/Regex.mjs | 2 +- .../operations/tests}/Register.mjs | 2 +- .../operations/tests}/RemoveDiacritics.mjs | 2 +- .../operations/tests}/Rotate.mjs | 2 +- .../operations/tests}/SeqUtils.mjs | 2 +- .../operations/tests}/SetDifference.mjs | 2 +- .../operations/tests}/SetIntersection.mjs | 2 +- .../operations/tests}/SetUnion.mjs | 2 +- .../operations/tests}/SplitColourChannels.mjs | 2 +- .../operations/tests}/StrUtils.mjs | 2 +- .../operations/tests}/SymmetricDifference.mjs | 2 +- .../tests}/TextEncodingBruteForce.mjs | 2 +- .../operations/tests}/ToGeohash.mjs | 2 +- .../tests}/TranslateDateTimeFormat.mjs | 2 +- 62 files changed, 122 insertions(+), 135 deletions(-) rename {test => tests/operations}/TestRegister.mjs (98%) rename {test => tests/operations}/index.mjs (52%) rename {test/tests/operations => tests/operations/tests}/BCD.mjs (98%) rename {test/tests/operations => tests/operations/tests}/BSON.mjs (96%) rename {test/tests/operations => tests/operations/tests}/Base58.mjs (98%) rename {test/tests/operations => tests/operations/tests}/Base62.mjs (97%) rename {test/tests/operations => tests/operations/tests}/Base64.mjs (98%) rename {test/tests/operations => tests/operations/tests}/BitwiseOp.mjs (97%) rename {test/tests/operations => tests/operations/tests}/ByteRepr.mjs (99%) rename {test/tests/operations => tests/operations/tests}/CSV.mjs (98%) rename {test/tests/operations => tests/operations/tests}/CartesianProduct.mjs (98%) rename {test/tests/operations => tests/operations/tests}/CharEnc.mjs (97%) rename {test/tests/operations => tests/operations/tests}/Checksum.mjs (98%) rename {test/tests/operations => tests/operations/tests}/Ciphers.mjs (99%) rename {test/tests/operations => tests/operations/tests}/Code.mjs (99%) rename {test/tests/operations => tests/operations/tests}/Comment.mjs (98%) rename {test/tests/operations => tests/operations/tests}/Compress.mjs (92%) rename {test/tests/operations => tests/operations/tests}/ConditionalJump.mjs (97%) rename {test/tests/operations => tests/operations/tests}/Crypt.mjs (99%) rename {test/tests/operations => tests/operations/tests}/DateTime.mjs (94%) rename {test/tests/operations => tests/operations/tests}/ExtractEmailAddresses.mjs (99%) rename {test/tests/operations => tests/operations/tests}/Fork.mjs (97%) rename {test/tests/operations => tests/operations/tests}/FromDecimal.mjs (93%) rename {test/tests/operations => tests/operations/tests}/FromGeohash.mjs (95%) rename {test/tests/operations => tests/operations/tests}/Hash.mjs (99%) rename {test/tests/operations => tests/operations/tests}/HaversineDistance.mjs (90%) rename {test/tests/operations => tests/operations/tests}/Hexdump.mjs (99%) rename {test/tests/operations => tests/operations/tests}/Image.mjs (99%) rename {test/tests/operations => tests/operations/tests}/JSONBeautify.mjs (98%) rename {test/tests/operations => tests/operations/tests}/JSONMinify.mjs (98%) rename {test/tests/operations => tests/operations/tests}/JWTDecode.mjs (96%) rename {test/tests/operations => tests/operations/tests}/JWTSign.mjs (98%) rename {test/tests/operations => tests/operations/tests}/JWTVerify.mjs (98%) rename {test/tests/operations => tests/operations/tests}/Jump.mjs (95%) rename {test/tests/operations => tests/operations/tests}/MS.mjs (92%) rename {test/tests/operations => tests/operations/tests}/Magic.mjs (97%) rename {test/tests/operations => tests/operations/tests}/Media.mjs (99%) rename {test/tests/operations => tests/operations/tests}/MorseCode.mjs (93%) rename {test/tests/operations => tests/operations/tests}/NetBIOS.mjs (93%) rename {test/tests/operations => tests/operations/tests}/OTP.mjs (91%) rename {test/tests/operations => tests/operations/tests}/PGP.mjs (99%) rename {test/tests/operations => tests/operations/tests}/PHP.mjs (97%) rename {test/tests/operations => tests/operations/tests}/ParseIPRange.mjs (99%) rename {test/tests/operations => tests/operations/tests}/ParseQRCode.mjs (99%) rename {test/tests/operations => tests/operations/tests}/ParseTLV.mjs (97%) rename {test/tests/operations => tests/operations/tests}/PowerSet.mjs (92%) rename {test/tests/operations => tests/operations/tests}/Regex.mjs (97%) rename {test/tests/operations => tests/operations/tests}/Register.mjs (98%) rename {test/tests/operations => tests/operations/tests}/RemoveDiacritics.mjs (94%) rename {test/tests/operations => tests/operations/tests}/Rotate.mjs (99%) rename {test/tests/operations => tests/operations/tests}/SeqUtils.mjs (96%) rename {test/tests/operations => tests/operations/tests}/SetDifference.mjs (96%) rename {test/tests/operations => tests/operations/tests}/SetIntersection.mjs (96%) rename {test/tests/operations => tests/operations/tests}/SetUnion.mjs (97%) rename {test/tests/operations => tests/operations/tests}/SplitColourChannels.mjs (99%) rename {test/tests/operations => tests/operations/tests}/StrUtils.mjs (99%) rename {test/tests/operations => tests/operations/tests}/SymmetricDifference.mjs (96%) rename {test/tests/operations => tests/operations/tests}/TextEncodingBruteForce.mjs (95%) rename {test/tests/operations => tests/operations/tests}/ToGeohash.mjs (95%) rename {test/tests/operations => tests/operations/tests}/TranslateDateTimeFormat.mjs (97%) diff --git a/Gruntfile.js b/Gruntfile.js index 61576278..06187f03 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -30,7 +30,7 @@ module.exports = function (grunt) { ["clean:node", "clean:config", "exec:generateConfig", "webpack:node", "chmod:build"]); grunt.registerTask("test", - "A task which runs all the tests in test/tests.", + "A task which runs all the tests in the tests directory.", ["exec:generateConfig", "exec:tests"]); grunt.registerTask("docs", @@ -148,7 +148,7 @@ module.exports = function (grunt) { core: ["src/core/**/*.{js,mjs}", "!src/core/vendor/**/*", "!src/core/operations/legacy/**/*"], web: ["src/web/**/*.{js,mjs}"], node: ["src/node/**/*.{js,mjs}"], - tests: ["test/**/*.{js,mjs}"], + tests: ["tests/**/*.{js,mjs}"], }, jsdoc: { options: { @@ -246,19 +246,6 @@ module.exports = function (grunt) { }), ] }, - tests: { - mode: "development", - target: "node", - entry: "./test/index.mjs", - externals: [NodeExternals()], - output: { - filename: "index.js", - path: __dirname + "/build/test" - }, - plugins: [ - new webpack.DefinePlugin(BUILD_CONSTANTS) - ] - }, node: { mode: "production", target: "node", @@ -400,7 +387,7 @@ module.exports = function (grunt) { ].join(";") }, tests: { - command: "node --experimental-modules --no-warnings --no-deprecation test/index.mjs" + command: "node --experimental-modules --no-warnings --no-deprecation tests/operations/index.mjs" } }, }); diff --git a/src/core/config/scripts/newOperation.mjs b/src/core/config/scripts/newOperation.mjs index 88b86296..ac25b383 100644 --- a/src/core/config/scripts/newOperation.mjs +++ b/src/core/config/scripts/newOperation.mjs @@ -222,7 +222,7 @@ export default ${moduleName}; console.log(`\nNext steps: 1. Add your operation to ${colors.green("src/core/config/Categories.json")} 2. Write your operation code. -3. Write tests in ${colors.green("test/tests/operations/")} +3. Write tests in ${colors.green("tests/operations/tests/")} 4. Run ${colors.cyan("npm run lint")} and ${colors.cyan("npm run test")} 5. Submit a Pull Request to get your operation added to the official CyberChef repository.`); diff --git a/test/TestRegister.mjs b/tests/operations/TestRegister.mjs similarity index 98% rename from test/TestRegister.mjs rename to tests/operations/TestRegister.mjs index 17b4c65a..f2b9f085 100644 --- a/test/TestRegister.mjs +++ b/tests/operations/TestRegister.mjs @@ -8,7 +8,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import Chef from "../src/core/Chef"; +import Chef from "../../src/core/Chef"; (function() { /** diff --git a/test/index.mjs b/tests/operations/index.mjs similarity index 52% rename from test/index.mjs rename to tests/operations/index.mjs index 9fe0407e..da9d41be 100644 --- a/test/index.mjs +++ b/tests/operations/index.mjs @@ -24,67 +24,67 @@ global.ENVIRONMENT_IS_WEB = function() { }; import TestRegister from "./TestRegister"; -import "./tests/operations/BCD"; -import "./tests/operations/BSON"; -import "./tests/operations/Base58"; -import "./tests/operations/Base64"; -import "./tests/operations/Base62"; -import "./tests/operations/BitwiseOp"; -import "./tests/operations/ByteRepr"; -import "./tests/operations/CartesianProduct"; -import "./tests/operations/CharEnc"; -import "./tests/operations/Checksum"; -import "./tests/operations/Ciphers"; -import "./tests/operations/Code"; -import "./tests/operations/Comment"; -import "./tests/operations/Compress"; -import "./tests/operations/ConditionalJump"; -import "./tests/operations/Crypt"; -import "./tests/operations/CSV"; -import "./tests/operations/DateTime"; -import "./tests/operations/ExtractEmailAddresses"; -import "./tests/operations/Fork"; -import "./tests/operations/FromDecimal"; -import "./tests/operations/FromGeohash"; -import "./tests/operations/Hash"; -import "./tests/operations/HaversineDistance"; -import "./tests/operations/Hexdump"; -import "./tests/operations/Image"; -import "./tests/operations/Jump"; -import "./tests/operations/JSONBeautify"; -import "./tests/operations/JSONMinify"; -import "./tests/operations/JWTDecode"; -import "./tests/operations/JWTSign"; -import "./tests/operations/JWTVerify"; -import "./tests/operations/MS"; -import "./tests/operations/Magic"; -import "./tests/operations/MorseCode"; -import "./tests/operations/NetBIOS"; -import "./tests/operations/OTP"; -import "./tests/operations/PGP"; -import "./tests/operations/PHP"; -import "./tests/operations/ParseIPRange"; -import "./tests/operations/ParseQRCode"; -import "./tests/operations/PowerSet"; -import "./tests/operations/Regex"; -import "./tests/operations/Register"; -import "./tests/operations/RemoveDiacritics"; -import "./tests/operations/Rotate"; -import "./tests/operations/SeqUtils"; -import "./tests/operations/SetDifference"; -import "./tests/operations/SetIntersection"; -import "./tests/operations/SetUnion"; -import "./tests/operations/StrUtils"; -import "./tests/operations/SymmetricDifference"; -import "./tests/operations/TextEncodingBruteForce"; -import "./tests/operations/ToGeohash"; -import "./tests/operations/TranslateDateTimeFormat"; -import "./tests/operations/Magic"; -import "./tests/operations/ParseTLV"; -import "./tests/operations/Media"; +import "./tests/BCD"; +import "./tests/BSON"; +import "./tests/Base58"; +import "./tests/Base64"; +import "./tests/Base62"; +import "./tests/BitwiseOp"; +import "./tests/ByteRepr"; +import "./tests/CartesianProduct"; +import "./tests/CharEnc"; +import "./tests/Checksum"; +import "./tests/Ciphers"; +import "./tests/Code"; +import "./tests/Comment"; +import "./tests/Compress"; +import "./tests/ConditionalJump"; +import "./tests/Crypt"; +import "./tests/CSV"; +import "./tests/DateTime"; +import "./tests/ExtractEmailAddresses"; +import "./tests/Fork"; +import "./tests/FromDecimal"; +import "./tests/FromGeohash"; +import "./tests/Hash"; +import "./tests/HaversineDistance"; +import "./tests/Hexdump"; +import "./tests/Image"; +import "./tests/Jump"; +import "./tests/JSONBeautify"; +import "./tests/JSONMinify"; +import "./tests/JWTDecode"; +import "./tests/JWTSign"; +import "./tests/JWTVerify"; +import "./tests/MS"; +import "./tests/Magic"; +import "./tests/MorseCode"; +import "./tests/NetBIOS"; +import "./tests/OTP"; +import "./tests/PGP"; +import "./tests/PHP"; +import "./tests/ParseIPRange"; +import "./tests/ParseQRCode"; +import "./tests/PowerSet"; +import "./tests/Regex"; +import "./tests/Register"; +import "./tests/RemoveDiacritics"; +import "./tests/Rotate"; +import "./tests/SeqUtils"; +import "./tests/SetDifference"; +import "./tests/SetIntersection"; +import "./tests/SetUnion"; +import "./tests/StrUtils"; +import "./tests/SymmetricDifference"; +import "./tests/TextEncodingBruteForce"; +import "./tests/ToGeohash"; +import "./tests/TranslateDateTimeFormat"; +import "./tests/Magic"; +import "./tests/ParseTLV"; +import "./tests/Media"; // Cannot test operations that use the File type yet -//import "./tests/operations/SplitColourChannels"; +//import "./tests/SplitColourChannels"; let allTestsPassing = true; const testStatusCounts = { diff --git a/test/tests/operations/BCD.mjs b/tests/operations/tests/BCD.mjs similarity index 98% rename from test/tests/operations/BCD.mjs rename to tests/operations/tests/BCD.mjs index 6f00abe4..87cbd53e 100644 --- a/test/tests/operations/BCD.mjs +++ b/tests/operations/tests/BCD.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/BSON.mjs b/tests/operations/tests/BSON.mjs similarity index 96% rename from test/tests/operations/BSON.mjs rename to tests/operations/tests/BSON.mjs index 2b99d845..f9eb6a87 100644 --- a/test/tests/operations/BSON.mjs +++ b/tests/operations/tests/BSON.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/Base58.mjs b/tests/operations/tests/Base58.mjs similarity index 98% rename from test/tests/operations/Base58.mjs rename to tests/operations/tests/Base58.mjs index 3b284223..0cc7612c 100644 --- a/test/tests/operations/Base58.mjs +++ b/tests/operations/tests/Base58.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/Base62.mjs b/tests/operations/tests/Base62.mjs similarity index 97% rename from test/tests/operations/Base62.mjs rename to tests/operations/tests/Base62.mjs index 8bf41b36..bb98348b 100644 --- a/test/tests/operations/Base62.mjs +++ b/tests/operations/tests/Base62.mjs @@ -7,7 +7,7 @@ * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/Base64.mjs b/tests/operations/tests/Base64.mjs similarity index 98% rename from test/tests/operations/Base64.mjs rename to tests/operations/tests/Base64.mjs index 5f725efe..d48c93c6 100644 --- a/test/tests/operations/Base64.mjs +++ b/tests/operations/tests/Base64.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; const ALL_BYTES = [ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", diff --git a/test/tests/operations/BitwiseOp.mjs b/tests/operations/tests/BitwiseOp.mjs similarity index 97% rename from test/tests/operations/BitwiseOp.mjs rename to tests/operations/tests/BitwiseOp.mjs index 7fdedded..09af70a1 100644 --- a/test/tests/operations/BitwiseOp.mjs +++ b/tests/operations/tests/BitwiseOp.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/ByteRepr.mjs b/tests/operations/tests/ByteRepr.mjs similarity index 99% rename from test/tests/operations/ByteRepr.mjs rename to tests/operations/tests/ByteRepr.mjs index 913755b8..86b6d58d 100644 --- a/test/tests/operations/ByteRepr.mjs +++ b/tests/operations/tests/ByteRepr.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; const ALL_BYTES = [ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", diff --git a/test/tests/operations/CSV.mjs b/tests/operations/tests/CSV.mjs similarity index 98% rename from test/tests/operations/CSV.mjs rename to tests/operations/tests/CSV.mjs index b3d79a05..28610058 100644 --- a/test/tests/operations/CSV.mjs +++ b/tests/operations/tests/CSV.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; const EXAMPLE_CSV = `A,B,C,D,E,F\r 1,2,3,4,5,6\r diff --git a/test/tests/operations/CartesianProduct.mjs b/tests/operations/tests/CartesianProduct.mjs similarity index 98% rename from test/tests/operations/CartesianProduct.mjs rename to tests/operations/tests/CartesianProduct.mjs index aafdb8b6..cb89ac5b 100644 --- a/test/tests/operations/CartesianProduct.mjs +++ b/tests/operations/tests/CartesianProduct.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/CharEnc.mjs b/tests/operations/tests/CharEnc.mjs similarity index 97% rename from test/tests/operations/CharEnc.mjs rename to tests/operations/tests/CharEnc.mjs index c7c5f0c0..d154ca3e 100644 --- a/test/tests/operations/CharEnc.mjs +++ b/tests/operations/tests/CharEnc.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/Checksum.mjs b/tests/operations/tests/Checksum.mjs similarity index 98% rename from test/tests/operations/Checksum.mjs rename to tests/operations/tests/Checksum.mjs index eac94038..9be19495 100644 --- a/test/tests/operations/Checksum.mjs +++ b/tests/operations/tests/Checksum.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; const BASIC_STRING = "The ships hung in the sky in much the same way that bricks don't."; const UTF8_STR = "ნუ პანიკას"; diff --git a/test/tests/operations/Ciphers.mjs b/tests/operations/tests/Ciphers.mjs similarity index 99% rename from test/tests/operations/Ciphers.mjs rename to tests/operations/tests/Ciphers.mjs index a46f50a9..f806e553 100644 --- a/test/tests/operations/Ciphers.mjs +++ b/tests/operations/tests/Ciphers.mjs @@ -7,7 +7,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ diff --git a/test/tests/operations/Code.mjs b/tests/operations/tests/Code.mjs similarity index 99% rename from test/tests/operations/Code.mjs rename to tests/operations/tests/Code.mjs index d9bda06f..048e13d6 100644 --- a/test/tests/operations/Code.mjs +++ b/tests/operations/tests/Code.mjs @@ -7,7 +7,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; const JSON_TEST_DATA = { "store": { diff --git a/test/tests/operations/Comment.mjs b/tests/operations/tests/Comment.mjs similarity index 98% rename from test/tests/operations/Comment.mjs rename to tests/operations/tests/Comment.mjs index 109fd761..2c033f56 100644 --- a/test/tests/operations/Comment.mjs +++ b/tests/operations/tests/Comment.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; const ALL_BYTES = [ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", diff --git a/test/tests/operations/Compress.mjs b/tests/operations/tests/Compress.mjs similarity index 92% rename from test/tests/operations/Compress.mjs rename to tests/operations/tests/Compress.mjs index 03a041ac..877b7215 100644 --- a/test/tests/operations/Compress.mjs +++ b/tests/operations/tests/Compress.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/ConditionalJump.mjs b/tests/operations/tests/ConditionalJump.mjs similarity index 97% rename from test/tests/operations/ConditionalJump.mjs rename to tests/operations/tests/ConditionalJump.mjs index 556440b2..b38c0d1b 100644 --- a/test/tests/operations/ConditionalJump.mjs +++ b/tests/operations/tests/ConditionalJump.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/Crypt.mjs b/tests/operations/tests/Crypt.mjs similarity index 99% rename from test/tests/operations/Crypt.mjs rename to tests/operations/tests/Crypt.mjs index 5e2a3321..60068ab3 100644 --- a/test/tests/operations/Crypt.mjs +++ b/tests/operations/tests/Crypt.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ /** diff --git a/test/tests/operations/DateTime.mjs b/tests/operations/tests/DateTime.mjs similarity index 94% rename from test/tests/operations/DateTime.mjs rename to tests/operations/tests/DateTime.mjs index fa19d4d9..3063ca64 100644 --- a/test/tests/operations/DateTime.mjs +++ b/tests/operations/tests/DateTime.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/ExtractEmailAddresses.mjs b/tests/operations/tests/ExtractEmailAddresses.mjs similarity index 99% rename from test/tests/operations/ExtractEmailAddresses.mjs rename to tests/operations/tests/ExtractEmailAddresses.mjs index 19782ec3..59365828 100644 --- a/test/tests/operations/ExtractEmailAddresses.mjs +++ b/tests/operations/tests/ExtractEmailAddresses.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/Fork.mjs b/tests/operations/tests/Fork.mjs similarity index 97% rename from test/tests/operations/Fork.mjs rename to tests/operations/tests/Fork.mjs index de6adf04..3752e340 100644 --- a/test/tests/operations/Fork.mjs +++ b/tests/operations/tests/Fork.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/FromDecimal.mjs b/tests/operations/tests/FromDecimal.mjs similarity index 93% rename from test/tests/operations/FromDecimal.mjs rename to tests/operations/tests/FromDecimal.mjs index b73292a8..b9b4de05 100644 --- a/test/tests/operations/FromDecimal.mjs +++ b/tests/operations/tests/FromDecimal.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2018 * @licence Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/FromGeohash.mjs b/tests/operations/tests/FromGeohash.mjs similarity index 95% rename from test/tests/operations/FromGeohash.mjs rename to tests/operations/tests/FromGeohash.mjs index 2ac68c58..9190ea69 100644 --- a/test/tests/operations/FromGeohash.mjs +++ b/tests/operations/tests/FromGeohash.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/Hash.mjs b/tests/operations/tests/Hash.mjs similarity index 99% rename from test/tests/operations/Hash.mjs rename to tests/operations/tests/Hash.mjs index ec4a6dac..31ebc3a6 100644 --- a/test/tests/operations/Hash.mjs +++ b/tests/operations/tests/Hash.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/HaversineDistance.mjs b/tests/operations/tests/HaversineDistance.mjs similarity index 90% rename from test/tests/operations/HaversineDistance.mjs rename to tests/operations/tests/HaversineDistance.mjs index dc10492b..ef0a298e 100644 --- a/test/tests/operations/HaversineDistance.mjs +++ b/tests/operations/tests/HaversineDistance.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/Hexdump.mjs b/tests/operations/tests/Hexdump.mjs similarity index 99% rename from test/tests/operations/Hexdump.mjs rename to tests/operations/tests/Hexdump.mjs index 3dc1ce7c..6b5c5043 100644 --- a/test/tests/operations/Hexdump.mjs +++ b/tests/operations/tests/Hexdump.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; const ALL_BYTES = [ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", diff --git a/test/tests/operations/Image.mjs b/tests/operations/tests/Image.mjs similarity index 99% rename from test/tests/operations/Image.mjs rename to tests/operations/tests/Image.mjs index a24cfa20..44f0af71 100644 --- a/test/tests/operations/Image.mjs +++ b/tests/operations/tests/Image.mjs @@ -7,7 +7,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/JSONBeautify.mjs b/tests/operations/tests/JSONBeautify.mjs similarity index 98% rename from test/tests/operations/JSONBeautify.mjs rename to tests/operations/tests/JSONBeautify.mjs index 5a89bd92..4388f82b 100644 --- a/test/tests/operations/JSONBeautify.mjs +++ b/tests/operations/tests/JSONBeautify.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/JSONMinify.mjs b/tests/operations/tests/JSONMinify.mjs similarity index 98% rename from test/tests/operations/JSONMinify.mjs rename to tests/operations/tests/JSONMinify.mjs index 4feefabf..dab396f1 100644 --- a/test/tests/operations/JSONMinify.mjs +++ b/tests/operations/tests/JSONMinify.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/JWTDecode.mjs b/tests/operations/tests/JWTDecode.mjs similarity index 96% rename from test/tests/operations/JWTDecode.mjs rename to tests/operations/tests/JWTDecode.mjs index 834fd5f2..df9b9bcc 100644 --- a/test/tests/operations/JWTDecode.mjs +++ b/tests/operations/tests/JWTDecode.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; const outputObject = JSON.stringify({ String: "SomeString", diff --git a/test/tests/operations/JWTSign.mjs b/tests/operations/tests/JWTSign.mjs similarity index 98% rename from test/tests/operations/JWTSign.mjs rename to tests/operations/tests/JWTSign.mjs index 36eff888..69787712 100644 --- a/test/tests/operations/JWTSign.mjs +++ b/tests/operations/tests/JWTSign.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; const inputObject = JSON.stringify({ String: "SomeString", diff --git a/test/tests/operations/JWTVerify.mjs b/tests/operations/tests/JWTVerify.mjs similarity index 98% rename from test/tests/operations/JWTVerify.mjs rename to tests/operations/tests/JWTVerify.mjs index bdf2c843..52b8574b 100644 --- a/test/tests/operations/JWTVerify.mjs +++ b/tests/operations/tests/JWTVerify.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; const outputObject = JSON.stringify({ String: "SomeString", diff --git a/test/tests/operations/Jump.mjs b/tests/operations/tests/Jump.mjs similarity index 95% rename from test/tests/operations/Jump.mjs rename to tests/operations/tests/Jump.mjs index 929432af..9bc98f31 100644 --- a/test/tests/operations/Jump.mjs +++ b/tests/operations/tests/Jump.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/MS.mjs b/tests/operations/tests/MS.mjs similarity index 92% rename from test/tests/operations/MS.mjs rename to tests/operations/tests/MS.mjs index f6018832..01618d90 100644 --- a/test/tests/operations/MS.mjs +++ b/tests/operations/tests/MS.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/Magic.mjs b/tests/operations/tests/Magic.mjs similarity index 97% rename from test/tests/operations/Magic.mjs rename to tests/operations/tests/Magic.mjs index d9b175d1..b7fd5ca3 100644 --- a/test/tests/operations/Magic.mjs +++ b/tests/operations/tests/Magic.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ diff --git a/test/tests/operations/Media.mjs b/tests/operations/tests/Media.mjs similarity index 99% rename from test/tests/operations/Media.mjs rename to tests/operations/tests/Media.mjs index 4d285e8d..96116167 100644 --- a/test/tests/operations/Media.mjs +++ b/tests/operations/tests/Media.mjs @@ -4,7 +4,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/MorseCode.mjs b/tests/operations/tests/MorseCode.mjs similarity index 93% rename from test/tests/operations/MorseCode.mjs rename to tests/operations/tests/MorseCode.mjs index ea8278ea..1da2cb78 100644 --- a/test/tests/operations/MorseCode.mjs +++ b/tests/operations/tests/MorseCode.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/NetBIOS.mjs b/tests/operations/tests/NetBIOS.mjs similarity index 93% rename from test/tests/operations/NetBIOS.mjs rename to tests/operations/tests/NetBIOS.mjs index 291412fe..f7210196 100644 --- a/test/tests/operations/NetBIOS.mjs +++ b/tests/operations/tests/NetBIOS.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/OTP.mjs b/tests/operations/tests/OTP.mjs similarity index 91% rename from test/tests/operations/OTP.mjs rename to tests/operations/tests/OTP.mjs index ccb215a4..a9198798 100644 --- a/test/tests/operations/OTP.mjs +++ b/tests/operations/tests/OTP.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/PGP.mjs b/tests/operations/tests/PGP.mjs similarity index 99% rename from test/tests/operations/PGP.mjs rename to tests/operations/tests/PGP.mjs index d2779f4f..baf76fb8 100644 --- a/test/tests/operations/PGP.mjs +++ b/tests/operations/tests/PGP.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; const ASCII_TEXT = "A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools."; diff --git a/test/tests/operations/PHP.mjs b/tests/operations/tests/PHP.mjs similarity index 97% rename from test/tests/operations/PHP.mjs rename to tests/operations/tests/PHP.mjs index 19a5bc86..dabd4d39 100644 --- a/test/tests/operations/PHP.mjs +++ b/tests/operations/tests/PHP.mjs @@ -7,7 +7,7 @@ * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/ParseIPRange.mjs b/tests/operations/tests/ParseIPRange.mjs similarity index 99% rename from test/tests/operations/ParseIPRange.mjs rename to tests/operations/tests/ParseIPRange.mjs index 1b81cbc0..83a8eea9 100644 --- a/test/tests/operations/ParseIPRange.mjs +++ b/tests/operations/tests/ParseIPRange.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/ParseQRCode.mjs b/tests/operations/tests/ParseQRCode.mjs similarity index 99% rename from test/tests/operations/ParseQRCode.mjs rename to tests/operations/tests/ParseQRCode.mjs index 55c402ae..191699d4 100644 --- a/test/tests/operations/ParseQRCode.mjs +++ b/tests/operations/tests/ParseQRCode.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/ParseTLV.mjs b/tests/operations/tests/ParseTLV.mjs similarity index 97% rename from test/tests/operations/ParseTLV.mjs rename to tests/operations/tests/ParseTLV.mjs index 43cc02d7..5617bf2b 100644 --- a/test/tests/operations/ParseTLV.mjs +++ b/tests/operations/tests/ParseTLV.mjs @@ -6,7 +6,7 @@ * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/PowerSet.mjs b/tests/operations/tests/PowerSet.mjs similarity index 92% rename from test/tests/operations/PowerSet.mjs rename to tests/operations/tests/PowerSet.mjs index f3fffed4..c742b030 100644 --- a/test/tests/operations/PowerSet.mjs +++ b/tests/operations/tests/PowerSet.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/Regex.mjs b/tests/operations/tests/Regex.mjs similarity index 97% rename from test/tests/operations/Regex.mjs rename to tests/operations/tests/Regex.mjs index a987bbeb..b48673dc 100644 --- a/test/tests/operations/Regex.mjs +++ b/tests/operations/tests/Regex.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/Register.mjs b/tests/operations/tests/Register.mjs similarity index 98% rename from test/tests/operations/Register.mjs rename to tests/operations/tests/Register.mjs index a66a5832..dec017dd 100644 --- a/test/tests/operations/Register.mjs +++ b/tests/operations/tests/Register.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/RemoveDiacritics.mjs b/tests/operations/tests/RemoveDiacritics.mjs similarity index 94% rename from test/tests/operations/RemoveDiacritics.mjs rename to tests/operations/tests/RemoveDiacritics.mjs index 5cc16da3..1b712ce3 100644 --- a/test/tests/operations/RemoveDiacritics.mjs +++ b/tests/operations/tests/RemoveDiacritics.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/Rotate.mjs b/tests/operations/tests/Rotate.mjs similarity index 99% rename from test/tests/operations/Rotate.mjs rename to tests/operations/tests/Rotate.mjs index d7ba9af1..83e39ee8 100644 --- a/test/tests/operations/Rotate.mjs +++ b/tests/operations/tests/Rotate.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ diff --git a/test/tests/operations/SeqUtils.mjs b/tests/operations/tests/SeqUtils.mjs similarity index 96% rename from test/tests/operations/SeqUtils.mjs rename to tests/operations/tests/SeqUtils.mjs index 4516298a..6947928d 100644 --- a/test/tests/operations/SeqUtils.mjs +++ b/tests/operations/tests/SeqUtils.mjs @@ -5,7 +5,7 @@ * @copyright Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/SetDifference.mjs b/tests/operations/tests/SetDifference.mjs similarity index 96% rename from test/tests/operations/SetDifference.mjs rename to tests/operations/tests/SetDifference.mjs index 3bc91d3f..1990d19d 100644 --- a/test/tests/operations/SetDifference.mjs +++ b/tests/operations/tests/SetDifference.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/SetIntersection.mjs b/tests/operations/tests/SetIntersection.mjs similarity index 96% rename from test/tests/operations/SetIntersection.mjs rename to tests/operations/tests/SetIntersection.mjs index 83809b6e..72d2529b 100644 --- a/test/tests/operations/SetIntersection.mjs +++ b/tests/operations/tests/SetIntersection.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/SetUnion.mjs b/tests/operations/tests/SetUnion.mjs similarity index 97% rename from test/tests/operations/SetUnion.mjs rename to tests/operations/tests/SetUnion.mjs index e997b0d4..1593d202 100644 --- a/test/tests/operations/SetUnion.mjs +++ b/tests/operations/tests/SetUnion.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/SplitColourChannels.mjs b/tests/operations/tests/SplitColourChannels.mjs similarity index 99% rename from test/tests/operations/SplitColourChannels.mjs rename to tests/operations/tests/SplitColourChannels.mjs index 9999aff5..60788425 100644 --- a/test/tests/operations/SplitColourChannels.mjs +++ b/tests/operations/tests/SplitColourChannels.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; // Base 85 encoded const testCard = "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"; const testCardSplit = "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"; diff --git a/test/tests/operations/StrUtils.mjs b/tests/operations/tests/StrUtils.mjs similarity index 99% rename from test/tests/operations/StrUtils.mjs rename to tests/operations/tests/StrUtils.mjs index 433593fc..515b5005 100644 --- a/test/tests/operations/StrUtils.mjs +++ b/tests/operations/tests/StrUtils.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2017 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/SymmetricDifference.mjs b/tests/operations/tests/SymmetricDifference.mjs similarity index 96% rename from test/tests/operations/SymmetricDifference.mjs rename to tests/operations/tests/SymmetricDifference.mjs index a2ef1562..d99783a3 100644 --- a/test/tests/operations/SymmetricDifference.mjs +++ b/tests/operations/tests/SymmetricDifference.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/TextEncodingBruteForce.mjs b/tests/operations/tests/TextEncodingBruteForce.mjs similarity index 95% rename from test/tests/operations/TextEncodingBruteForce.mjs rename to tests/operations/tests/TextEncodingBruteForce.mjs index 22e8f7c5..74408576 100644 --- a/test/tests/operations/TextEncodingBruteForce.mjs +++ b/tests/operations/tests/TextEncodingBruteForce.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/ToGeohash.mjs b/tests/operations/tests/ToGeohash.mjs similarity index 95% rename from test/tests/operations/ToGeohash.mjs rename to tests/operations/tests/ToGeohash.mjs index b50e7280..30f7337e 100644 --- a/test/tests/operations/ToGeohash.mjs +++ b/tests/operations/tests/ToGeohash.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { diff --git a/test/tests/operations/TranslateDateTimeFormat.mjs b/tests/operations/tests/TranslateDateTimeFormat.mjs similarity index 97% rename from test/tests/operations/TranslateDateTimeFormat.mjs rename to tests/operations/tests/TranslateDateTimeFormat.mjs index d991f8be..a60459fd 100644 --- a/test/tests/operations/TranslateDateTimeFormat.mjs +++ b/tests/operations/tests/TranslateDateTimeFormat.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../../TestRegister"; +import TestRegister from "../TestRegister"; TestRegister.addTests([ { From b0fb9db4b8f47f96cdf3d85c72098ac2556932c5 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Sat, 29 Dec 2018 02:58:05 +0000 Subject: [PATCH 042/147] Added nightwatch.js test suite for confirming that the app loads correctly and can run operations from each module. Currently only support the latest version of Chrome. --- .editorconfig | 2 +- .eslintrc.json | 9 + .gitignore | 1 + Gruntfile.js | 13 +- nightwatch.json | 27 ++ package-lock.json | 678 ++++++++++++++++++++++++++++++++++++ package.json | 3 + tests/browser/nightwatch.js | 181 ++++++++++ 8 files changed, 910 insertions(+), 4 deletions(-) create mode 100644 nightwatch.json create mode 100644 tests/browser/nightwatch.js diff --git a/.editorconfig b/.editorconfig index a523a504..b50059bb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,6 +9,6 @@ trim_trailing_whitespace = true indent_style = space indent_size = 4 -[{package.json,.travis.yml}] +[{package.json,.travis.yml,nightwatch.json}] indent_style = space indent_size = 2 diff --git a/.eslintrc.json b/.eslintrc.json index e512df1b..715a9629 100755 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -87,6 +87,15 @@ "no-var": "error", "prefer-const": "error" }, + "overrides": [ + { + "files": "tests/**/*", + "rules": { + "no-unused-expressions": "off", + "no-console": "off" + } + } + ], "globals": { "$": false, "jQuery": false, diff --git a/.gitignore b/.gitignore index 9ea869e3..3ca816f6 100755 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ docs/* src/core/config/modules/* src/core/config/OperationConfig.json src/core/operations/index.mjs +tests/browser/output/* diff --git a/Gruntfile.js b/Gruntfile.js index 06187f03..4f02d1be 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -30,8 +30,12 @@ module.exports = function (grunt) { ["clean:node", "clean:config", "exec:generateConfig", "webpack:node", "chmod:build"]); grunt.registerTask("test", - "A task which runs all the tests in the tests directory.", - ["exec:generateConfig", "exec:tests"]); + "A task which runs all the operation tests in the tests directory.", + ["exec:generateConfig", "exec:opTests"]); + + grunt.registerTask("testui", + "A task which runs all the UI tests in the tests directory. Requires the dev server to be running.", + ["exec:browserTests"]); grunt.registerTask("docs", "Compiles documentation in the /docs directory.", @@ -386,8 +390,11 @@ module.exports = function (grunt) { "echo '--- Config scripts finished. ---\n'" ].join(";") }, - tests: { + opTests: { command: "node --experimental-modules --no-warnings --no-deprecation tests/operations/index.mjs" + }, + browserTests: { + command: "./node_modules/.bin/nightwatch --env chrome" } }, }); diff --git a/nightwatch.json b/nightwatch.json new file mode 100644 index 00000000..3aa57d9b --- /dev/null +++ b/nightwatch.json @@ -0,0 +1,27 @@ +{ + "src_folders": ["tests/browser"], + "output_folder": "tests/browser/output", + + "test_settings": { + + "default": { + "launch_url": "http://localhost:8080", + "webdriver": { + "start_process": true, + "log_path": false + } + }, + + "chrome": { + "webdriver": { + "server_path": "./node_modules/.bin/chromedriver", + "port": 9515 + }, + "desiredCapabilities": { + "browserName": "chrome" + } + } + + } +} + diff --git a/package-lock.json b/package-lock.json index a4c4355c..e925396d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1440,6 +1440,26 @@ "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", "dev": true }, + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + }, + "dependencies": { + "es6-promisify": { + "version": "5.0.0", + "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + } + } + }, "ajv": { "version": "6.5.5", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.5.tgz", @@ -1695,12 +1715,24 @@ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "ast-types": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.7.tgz", + "integrity": "sha512-2mP3TwtkY/aTv5X3ZsMpNAbOnyoC/aMJwJSoaELPkHId0nSQgFcnU4dRW3isxiz7+zBexk0ym3WNVjMiQBnJSw==", + "dev": true + }, "async": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", @@ -2182,6 +2214,13 @@ "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", "dev": true }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "optional": true + }, "browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", @@ -2447,6 +2486,24 @@ "underscore-contrib": "~0.3.0" } }, + "chai-nightwatch": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/chai-nightwatch/-/chai-nightwatch-0.2.1.tgz", + "integrity": "sha512-2lprSMi72sHq2ZGyPTYUDQNsd2O4z81SicascbI4bkU54Xzk5Ofunn2CbrExADGC7jBH2D8r66X/aSEl+/agXQ==", + "dev": true, + "requires": { + "assertion-error": "1.0.0", + "deep-eql": "0.1.3" + }, + "dependencies": { + "assertion-error": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", + "integrity": "sha1-x/hUOP3UZrx8oWq5DIFRN5el0js=", + "dev": true + } + } + }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", @@ -2515,6 +2572,19 @@ "tslib": "^1.9.0" } }, + "chromedriver": { + "version": "2.45.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.45.0.tgz", + "integrity": "sha512-Qwmcr+2mU3INeR6mVsQ8gO00vZpL8ZeTJLclX44C0dcs88jrSDgckPqbG+qkVX+m2L/aOPnF0lYgPdOiOiLt5w==", + "dev": true, + "requires": { + "del": "^3.0.0", + "extract-zip": "^1.6.7", + "mkdirp": "^0.5.1", + "request": "^2.88.0", + "tcp-port-used": "^1.0.1" + } + }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -2638,6 +2708,12 @@ "shallow-clone": "^1.0.0" } }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -3170,6 +3246,12 @@ "assert-plus": "^1.0.0" } }, + "data-uri-to-buffer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", + "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==", + "dev": true + }, "data-urls": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", @@ -3241,6 +3323,15 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, + "deep-eql": { + "version": "0.1.3", + "resolved": "http://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "dev": true, + "requires": { + "type-detect": "0.1.1" + } + }, "deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", @@ -3326,6 +3417,25 @@ } } }, + "degenerator": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", + "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", + "dev": true, + "requires": { + "ast-types": "0.x.x", + "escodegen": "1.x.x", + "esprima": "3.x.x" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + } + } + }, "del": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", @@ -4478,6 +4588,12 @@ "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz", "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==" }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true + }, "filesize": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", @@ -5313,6 +5429,24 @@ "rimraf": "2" } }, + "ftp": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", + "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", + "dev": true, + "requires": { + "readable-stream": "1.1.x", + "xregexp": "2.0.0" + }, + "dependencies": { + "xregexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", + "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", + "dev": true + } + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -5395,6 +5529,52 @@ "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, + "get-uri": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.2.tgz", + "integrity": "sha512-ZD325dMZOgerGqF/rF6vZXyFGTAay62svjQIT+X/oU2PtxYpFxvSkbsdi+oxIrsNxlZVd4y8wUDqkaExWTI/Cw==", + "dev": true, + "requires": { + "data-uri-to-buffer": "1", + "debug": "2", + "extend": "3", + "file-uri-to-path": "1", + "ftp": "~0.3.10", + "readable-stream": "2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -5510,6 +5690,13 @@ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", "dev": true }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "optional": true + }, "grunt": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.3.tgz", @@ -6179,6 +6366,27 @@ "requires-port": "^1.0.0" } }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "dev": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, "http-proxy-middleware": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", @@ -6208,6 +6416,33 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "dev": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, "i": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", @@ -6831,6 +7066,12 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -6849,6 +7090,17 @@ "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", "dev": true }, + "is2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.1.tgz", + "integrity": "sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "ip-regex": "^2.1.0", + "is-url": "^1.2.2" + } + }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -7463,12 +7715,89 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, + "lodash._arraycopy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz", + "integrity": "sha1-due3wfH7klRzdIeKVi7Qaj5Q9uE=", + "dev": true + }, + "lodash._arrayeach": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz", + "integrity": "sha1-urFWsqkNPxu9XGU0AzSeXlkz754=", + "dev": true + }, + "lodash._baseassign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "dev": true, + "requires": { + "lodash._basecopy": "^3.0.0", + "lodash.keys": "^3.0.0" + } + }, + "lodash._baseclone": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz", + "integrity": "sha1-MDUZv2OT/n5C802LYw73eU41Qrc=", + "dev": true, + "requires": { + "lodash._arraycopy": "^3.0.0", + "lodash._arrayeach": "^3.0.0", + "lodash._baseassign": "^3.0.0", + "lodash._basefor": "^3.0.0", + "lodash.isarray": "^3.0.0", + "lodash.keys": "^3.0.0" + } + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basefor": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz", + "integrity": "sha1-dVC06SGO8J+tJDQ7YSAhx5tMIMI=", + "dev": true + }, + "lodash._bindcallback": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", + "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, "lodash.assign": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", "dev": true }, + "lodash.clone": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-3.0.3.tgz", + "integrity": "sha1-hGiMc9MrWpDKJWFpY/GJJSqZcEM=", + "dev": true, + "requires": { + "lodash._baseclone": "^3.0.0", + "lodash._bindcallback": "^3.0.0", + "lodash._isiterateecall": "^3.0.0" + } + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -7481,6 +7810,12 @@ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, + "lodash.defaultsdeep": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.0.tgz", + "integrity": "sha1-vsECT4WxvZbL6kBbI8FK1kQ6b4E=", + "dev": true + }, "lodash.escaperegexp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", @@ -7492,6 +7827,18 @@ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, "lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", @@ -7517,6 +7864,23 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "lodash.merge": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", + "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==", + "dev": true + }, "lodash.mergewith": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", @@ -7909,6 +8273,83 @@ } } }, + "mkpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mkpath/-/mkpath-1.0.0.tgz", + "integrity": "sha1-67Opd+evHGg65v2hK1Raa6bFhT0=", + "dev": true + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "optional": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + }, + "dependencies": { + "commander": { + "version": "2.15.1", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true, + "optional": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true, + "optional": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "optional": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "moment": { "version": "2.22.2", "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", @@ -8032,6 +8473,12 @@ "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", "dev": true }, + "netmask": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", + "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=", + "dev": true + }, "ngeohash": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/ngeohash/-/ngeohash-0.6.0.tgz", @@ -8043,6 +8490,36 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "nightwatch": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/nightwatch/-/nightwatch-1.0.17.tgz", + "integrity": "sha512-/du74poqA8JNKkgpo00sRxomfKwVtkgkIQ9S66VWK5AWsRRYYzh0wmCMMFjZm2EfagECymP395xwUZ+BW1K9qg==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "chai-nightwatch": "0.2.1", + "ejs": "^2.5.9", + "lodash.clone": "^3.0.3", + "lodash.defaultsdeep": "^4.6.0", + "lodash.merge": "^4.6.1", + "minimatch": "3.0.3", + "mkpath": "1.0.0", + "mocha": "^5.1.1", + "optimist": "^0.6.1", + "proxy-agent": "^3.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", + "dev": true, + "requires": { + "brace-expansion": "^1.0.0" + } + } + } + }, "no-case": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", @@ -8458,6 +8935,30 @@ "is-wsl": "^1.1.0" } }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, "optionator": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", @@ -8573,6 +9074,79 @@ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, + "pac-proxy-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-3.0.0.tgz", + "integrity": "sha512-AOUX9jES/EkQX2zRz0AW7lSx9jD//hQS8wFXBvcnd/J2Py9KaMJMqV/LPqJssj1tgGufotb2mmopGPR15ODv1Q==", + "dev": true, + "requires": { + "agent-base": "^4.2.0", + "debug": "^3.1.0", + "get-uri": "^2.0.0", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "pac-resolver": "^3.0.0", + "raw-body": "^2.2.0", + "socks-proxy-agent": "^4.0.1" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + } + } + }, + "pac-resolver": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-3.0.0.tgz", + "integrity": "sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA==", + "dev": true, + "requires": { + "co": "^4.6.0", + "degenerator": "^1.0.4", + "ip": "^1.1.5", + "netmask": "^1.0.6", + "thunkify": "^2.1.2" + } + }, "pad-stream": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pad-stream/-/pad-stream-1.2.0.tgz", @@ -9427,6 +10001,45 @@ "ipaddr.js": "1.8.0" } }, + "proxy-agent": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.3.tgz", + "integrity": "sha512-PXVVVuH9tiQuxQltFJVSnXWuDtNr+8aNBP6XVDDCDiUuDN8eRCm+ii4/mFWmXWEA0w8jjJSlePa4LXlM4jIzNA==", + "dev": true, + "requires": { + "agent-base": "^4.2.0", + "debug": "^3.1.0", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "lru-cache": "^4.1.2", + "pac-proxy-agent": "^3.0.0", + "proxy-from-env": "^1.0.0", + "socks-proxy-agent": "^4.0.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", + "dev": true + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -10510,6 +11123,12 @@ "is-fullwidth-code-point": "^2.0.0" } }, + "smart-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.1.tgz", + "integrity": "sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==", + "dev": true + }, "snackbarjs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/snackbarjs/-/snackbarjs-1.1.0.tgz", @@ -10678,6 +11297,26 @@ } } }, + "socks": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.2.tgz", + "integrity": "sha512-g6wjBnnMOZpE0ym6e0uHSddz9p3a+WsBaaYQaBaSCJYvrC4IXykQR9MNGjLQf38e9iIIhp3b1/Zk8YZI3KGJ0Q==", + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.0.1" + } + }, + "socks-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz", + "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==", + "dev": true, + "requires": { + "agent-base": "~4.2.0", + "socks": "~2.2.0" + } + }, "sortablejs": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.7.0.tgz", @@ -11237,6 +11876,33 @@ "inherits": "2" } }, + "tcp-port-used": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.1.tgz", + "integrity": "sha512-rwi5xJeU6utXoEIiMvVBMc9eJ2/ofzB+7nLOdnZuFTmNCLqRiQh2sMG9MqCxHU/69VC/Fwp5dV9306Qd54ll1Q==", + "dev": true, + "requires": { + "debug": "4.1.0", + "is2": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", + "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -11302,6 +11968,12 @@ } } }, + "thunkify": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", + "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=", + "dev": true + }, "thunky": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", @@ -11532,6 +12204,12 @@ "prelude-ls": "~1.1.2" } }, + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", + "dev": true + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", diff --git a/package.json b/package.json index 43ad47ed..1a2065f2 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "autoprefixer": "^9.3.1", "babel-loader": "^8.0.4", "bootstrap": "^4.1.3", + "chromedriver": "^2.45.0", "colors": "^1.3.2", "css-loader": "^1.0.1", "eslint": "^5.8.0", @@ -56,6 +57,7 @@ "imports-loader": "^0.8.0", "ink-docstrap": "^1.3.2", "jsdoc-babel": "^0.5.0", + "nightwatch": "^1.0.17", "node-sass": "^4.10.0", "postcss-css-variables": "^0.11.0", "postcss-import": "^12.0.1", @@ -133,6 +135,7 @@ "start": "grunt dev", "build": "grunt prod", "test": "grunt test", + "testui": "grunt testui", "docs": "grunt docs", "lint": "grunt lint", "newop": "node --experimental-modules src/core/config/scripts/newOperation.mjs" diff --git a/tests/browser/nightwatch.js b/tests/browser/nightwatch.js new file mode 100644 index 00000000..82780427 --- /dev/null +++ b/tests/browser/nightwatch.js @@ -0,0 +1,181 @@ +/** + * Tests to ensure that the app loads correctly in a reasonable time and that operations can br run. + * + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +module.exports = { + before: function (browser) { + console.log("The dev server much be running on http://localhost:8080 for these tests to work.") + browser + .resizeWindow(1280, 800) + .url(browser.launchUrl); + }, + + "Loading screen": browser => { + // Check that the loading screen appears and then disappears within a reasonable time + browser + .waitForElementVisible("#preloader", 300) + .waitForElementNotPresent("#preloader", 10000); + }, + + "App loaded": browser => { + browser.useCss(); + // Check that various important elements are loaded + browser.expect.element("#operations").to.be.visible; + browser.expect.element("#recipe").to.be.visible; + browser.expect.element("#input").to.be.present; + browser.expect.element("#output").to.be.present; + browser.expect.element(".op-list").to.be.present; + browser.expect.element("#rec-list").to.be.visible; + browser.expect.element("#controls").to.be.visible; + browser.expect.element("#input-text").to.be.visible; + browser.expect.element("#output-text").to.be.visible; + }, + + "Operations loaded": browser => { + browser.useXpath(); + // Check an operation in every category + browser.expect.element("//li[contains(@class, 'operation') and text()='To Base64']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='To Binary']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='AES Decrypt']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='PEM to Hex']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='Power Set']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='Parse IP range']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='Remove Diacritics']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='Sort']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='To UNIX Timestamp']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='Extract dates']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='Gzip']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='Keccak']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='JSON Beautify']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='Detect File Type']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='Play Media']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='Disassemble x86']").to.be.present; + browser.expect.element("//li[contains(@class, 'operation') and text()='Register']").to.be.present; + }, + + "Recipe can be run": browser => { + const toHex = "//li[contains(@class, 'operation') and text()='To Hex']"; + const op = "#rec-list .operation .op-title"; + + // Check that operation is visible + browser + .useXpath() + .expect.element(toHex).to.be.visible; + + // Add it to the recipe by double clicking + browser + .useXpath() + .moveToElement(toHex, 10, 10) + .useCss() + .waitForElementVisible(".popover-body", 500) + .doubleClick() + .waitForElementVisible(op); + + // Confirm that it has been added to the recipe + browser + .useCss() + .expect.element(op).text.to.contain("To Hex"); + + // Enter input + browser + .useCss() + .setValue("#input-text", "Don't Panic."); + + // Check output + browser + .useCss() + .expect.element("#output-text").to.have.value.that.equals("44 6f 6e 27 74 20 50 61 6e 69 63 2e"); + + // Clear recipe + browser + .useCss() + .moveToElement(op, 10, 10) + .waitForElementNotPresent(".popover-body", 500) + .click("#clr-recipe") + .waitForElementNotPresent(op); + }, + + "Test every module": browser => { + browser.useCss(); + + // BSON + loadOp("BSON deserialise", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // Ciphers + loadOp("AES Encrypt", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // Code + loadOp("XPath expression", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // Compression + loadOp("Gzip", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // Crypto + loadOp("MD5", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // Default + loadOp("Fork", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // Diff + loadOp("Diff", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // Encodings + loadOp("Encode text", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // Image + loadOp("Extract EXIF", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // PGP + loadOp("PGP Encrypt", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // PublicKey + loadOp("Hex to PEM", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // Regex + loadOp("Strings", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // Shellcode + loadOp("Disassemble x86", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // URL + loadOp("URL Encode", browser) + .waitForElementNotVisible("#output-loader", 5000); + + // UserAgent + loadOp("Parse User Agent", browser) + .waitForElementNotVisible("#output-loader", 5000); + }, + + after: browser => { + browser.end(); + } +}; + +/** + * Clears the current recipe and loads a new operation. + * + * @param {string} opName + * @param {Browser} browser + */ +function loadOp(opName, browser) { + return browser + .useCss() + .click("#clr-recipe") + .urlHash("op=" + opName); +} From b631e3fef622a5f7fb872c9ce994887ff9702dd4 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Sat, 29 Dec 2018 23:46:13 +0000 Subject: [PATCH 043/147] Added nightwatch tests to TravisCI build process for prod and inline versions. --- .travis.yml | 1 + Gruntfile.js | 15 ++- nightwatch.json | 21 ++-- package-lock.json | 201 ++++++++++++++++++++++++++++++++++++ package.json | 1 + tests/browser/nightwatch.js | 1 - 6 files changed, 229 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index bf4549e2..ead7ab74 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ script: - grunt docs - grunt node - grunt prod --msg="$COMPILE_MSG" + - xvfb-run --server-args="-screen 0 1200x800x24" grunt testui before_deploy: - grunt exec:sitemap - grunt copy:ghPages diff --git a/Gruntfile.js b/Gruntfile.js index 4f02d1be..6a5799be 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -35,7 +35,7 @@ module.exports = function (grunt) { grunt.registerTask("testui", "A task which runs all the UI tests in the tests directory. Requires the dev server to be running.", - ["exec:browserTests"]); + ["connect:prod", "exec:browserTests"]); grunt.registerTask("docs", "Compiles documentation in the /docs directory.", @@ -71,6 +71,7 @@ module.exports = function (grunt) { grunt.loadNpmTasks("grunt-exec"); grunt.loadNpmTasks("grunt-accessibility"); grunt.loadNpmTasks("grunt-concurrent"); + grunt.loadNpmTasks("grunt-contrib-connect"); // Project configuration @@ -148,7 +149,7 @@ module.exports = function (grunt) { options: { configFile: "./.eslintrc.json" }, - configs: ["*.js"], + configs: ["*.{js,mjs}"], core: ["src/core/**/*.{js,mjs}", "!src/core/vendor/**/*", "!src/core/operations/legacy/**/*"], web: ["src/web/**/*.{js,mjs}"], node: ["src/node/**/*.{js,mjs}"], @@ -311,6 +312,14 @@ module.exports = function (grunt) { } } }, + connect: { + prod: { + options: { + port: 8000, + base: "build/prod/" + } + } + }, copy: { ghPages: { options: { @@ -394,7 +403,7 @@ module.exports = function (grunt) { command: "node --experimental-modules --no-warnings --no-deprecation tests/operations/index.mjs" }, browserTests: { - command: "./node_modules/.bin/nightwatch --env chrome" + command: "./node_modules/.bin/nightwatch --env prod,inline" } }, }); diff --git a/nightwatch.json b/nightwatch.json index 3aa57d9b..e9c1ebef 100644 --- a/nightwatch.json +++ b/nightwatch.json @@ -8,18 +8,25 @@ "launch_url": "http://localhost:8080", "webdriver": { "start_process": true, - "log_path": false - } - }, - - "chrome": { - "webdriver": { "server_path": "./node_modules/.bin/chromedriver", - "port": 9515 + "port": 9515, + "log_path": false }, "desiredCapabilities": { "browserName": "chrome" } + }, + + "dev": { + "launch_url": "http://localhost:8080" + }, + + "prod": { + "launch_url": "http://localhost:8000/index.html" + }, + + "inline": { + "launch_url": "http://localhost:8000/cyberchef.htm" } } diff --git a/package-lock.json b/package-lock.json index e925396d..d5d75248 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1983,6 +1983,15 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -2865,12 +2874,53 @@ } } }, + "connect": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", + "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.0", + "parseurl": "~1.3.2", + "utils-merge": "1.0.1" + }, + "dependencies": { + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", + "unpipe": "~1.0.0" + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true + } + } + }, "connect-history-api-fallback": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", "integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=", "dev": true }, + "connect-livereload": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.6.1.tgz", + "integrity": "sha512-3R0kMOdL7CjJpU66fzAkCe6HNtd3AavCS4m+uW4KtJjrdGPT0SQEZieAYd+cm+lJoBznNQ4lqipYWkhBMgk00g==", + "dev": true + }, "console-browserify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", @@ -5828,6 +5878,23 @@ "rimraf": "^2.6.2" } }, + "grunt-contrib-connect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-connect/-/grunt-contrib-connect-2.0.0.tgz", + "integrity": "sha512-JVjM9UDP84WbT2S7swkyuwPuxFtT+zry/RUBuP3IT8LZPEQjtzzMwiM+qimswNKQ9plh5WhcFWaaqz2ruB9/DA==", + "dev": true, + "requires": { + "async": "^2.6.1", + "connect": "^3.6.6", + "connect-livereload": "^0.6.0", + "morgan": "^1.9.0", + "node-http2": "^4.0.1", + "opn": "^5.3.0", + "portscanner": "^2.2.0", + "serve-index": "^1.9.1", + "serve-static": "^1.13.2" + } + }, "grunt-contrib-copy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz", @@ -6991,6 +7058,15 @@ } } }, + "is-number-like": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", + "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", + "dev": true, + "requires": { + "lodash.isfinite": "^3.3.2" + } + }, "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -7844,6 +7920,12 @@ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" }, + "lodash.isfinite": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", + "integrity": "sha1-+4m2WpqAKBgz8LdHizpRBPiY67M=", + "dev": true + }, "lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -8371,6 +8453,19 @@ "iced-runtime": ">=0.0.1" } }, + "morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "dev": true, + "requires": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + } + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -8562,6 +8657,39 @@ } } }, + "node-http2": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/node-http2/-/node-http2-4.0.1.tgz", + "integrity": "sha1-Fk/1O13SLITwrxQrh3xerraAmVk=", + "dev": true, + "requires": { + "assert": "1.4.1", + "events": "1.1.1", + "https-browserify": "0.0.1", + "setimmediate": "^1.0.5", + "stream-browserify": "2.0.1", + "timers-browserify": "2.0.2", + "url": "^0.11.0", + "websocket-stream": "^5.0.1" + }, + "dependencies": { + "https-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", + "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=", + "dev": true + }, + "timers-browserify": { + "version": "2.0.2", + "resolved": "http://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.2.tgz", + "integrity": "sha1-q0iDz1l9zVCvIRNJoA+8pWrIa4Y=", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + } + } + }, "node-libs-browser": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", @@ -9535,6 +9663,16 @@ } } }, + "portscanner": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", + "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", + "dev": true, + "requires": { + "async": "^2.6.0", + "is-number-like": "^1.0.3" + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -12279,6 +12417,12 @@ "resolved": "https://registry.npmjs.org/uint64be/-/uint64be-1.0.1.tgz", "integrity": "sha1-H3FUIC8qG4rzU4cd2mUb80zpPpU=" }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, "underscore": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", @@ -13178,6 +13322,63 @@ "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", "dev": true }, + "websocket-stream": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.1.2.tgz", + "integrity": "sha512-lchLOk435iDWs0jNuL+hiU14i3ERSrMA0IKSiJh7z6X/i4XNsutBZrtqu2CPOZuA4G/zabiqVAos0vW+S7GEVw==", + "dev": true, + "requires": { + "duplexify": "^3.5.1", + "inherits": "^2.0.1", + "readable-stream": "^2.3.3", + "safe-buffer": "^5.1.1", + "ws": "^3.2.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + } + } + }, "whatwg-encoding": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", diff --git a/package.json b/package.json index 1a2065f2..8353acd5 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "grunt-chmod": "~1.1.1", "grunt-concurrent": "^2.3.1", "grunt-contrib-clean": "~2.0.0", + "grunt-contrib-connect": "^2.0.0", "grunt-contrib-copy": "~1.0.0", "grunt-contrib-watch": "^1.1.0", "grunt-eslint": "^21.0.0", diff --git a/tests/browser/nightwatch.js b/tests/browser/nightwatch.js index 82780427..36e4af21 100644 --- a/tests/browser/nightwatch.js +++ b/tests/browser/nightwatch.js @@ -7,7 +7,6 @@ */ module.exports = { before: function (browser) { - console.log("The dev server much be running on http://localhost:8080 for these tests to work.") browser .resizeWindow(1280, 800) .url(browser.launchUrl); From f7707faece61655c67974a9c8509e08bae84ba25 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Sun, 30 Dec 2018 00:02:41 +0000 Subject: [PATCH 044/147] Added Chrome to TravisCI config --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index ead7ab74..9e76ec72 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: node_js node_js: - node +addons: + chrome: stable install: npm install before_script: - npm install -g grunt From 840e44deacdd06f4895c35c6711d50284b0d7be0 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Sun, 30 Dec 2018 00:26:28 +0000 Subject: [PATCH 045/147] Tidied up UI tests --- Gruntfile.js | 2 +- tests/browser/nightwatch.js | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 6a5799be..dd890982 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -34,7 +34,7 @@ module.exports = function (grunt) { ["exec:generateConfig", "exec:opTests"]); grunt.registerTask("testui", - "A task which runs all the UI tests in the tests directory. Requires the dev server to be running.", + "A task which runs all the UI tests in the tests directory. The prod task must already have been run.", ["connect:prod", "exec:browserTests"]); grunt.registerTask("docs", diff --git a/tests/browser/nightwatch.js b/tests/browser/nightwatch.js index 36e4af21..fe8da2c5 100644 --- a/tests/browser/nightwatch.js +++ b/tests/browser/nightwatch.js @@ -1,12 +1,12 @@ /** - * Tests to ensure that the app loads correctly in a reasonable time and that operations can br run. + * Tests to ensure that the app loads correctly in a reasonable time and that operations can be run. * * @author n1474335 [n1474335@gmail.com] * @copyright Crown Copyright 2018 * @license Apache-2.0 */ module.exports = { - before: function (browser) { + before: browser => { browser .resizeWindow(1280, 800) .url(browser.launchUrl); @@ -35,7 +35,7 @@ module.exports = { "Operations loaded": browser => { browser.useXpath(); - // Check an operation in every category + // Check that an operation in every category has been populated browser.expect.element("//li[contains(@class, 'operation') and text()='To Base64']").to.be.present; browser.expect.element("//li[contains(@class, 'operation') and text()='To Binary']").to.be.present; browser.expect.element("//li[contains(@class, 'operation') and text()='AES Decrypt']").to.be.present; @@ -70,12 +70,12 @@ module.exports = { .moveToElement(toHex, 10, 10) .useCss() .waitForElementVisible(".popover-body", 500) - .doubleClick() - .waitForElementVisible(op); + .doubleClick(); // Confirm that it has been added to the recipe browser .useCss() + .waitForElementVisible(op) .expect.element(op).text.to.contain("To Hex"); // Enter input @@ -86,6 +86,7 @@ module.exports = { // Check output browser .useCss() + .waitForElementNotVisible("#stale-indicator", 100) .expect.element("#output-text").to.have.value.that.equals("44 6f 6e 27 74 20 50 61 6e 69 63 2e"); // Clear recipe From 7f2e879e2418b8267a8e2380b1e650c7e31221b7 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Sun, 30 Dec 2018 00:37:44 +0000 Subject: [PATCH 046/147] Added explicit bake after input added in test suite. --- tests/browser/nightwatch.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/browser/nightwatch.js b/tests/browser/nightwatch.js index fe8da2c5..dd8401fe 100644 --- a/tests/browser/nightwatch.js +++ b/tests/browser/nightwatch.js @@ -81,7 +81,8 @@ module.exports = { // Enter input browser .useCss() - .setValue("#input-text", "Don't Panic."); + .setValue("#input-text", "Don't Panic.") + .click("#bake"); // Check output browser From 71e0a4e0cec68fe4ac7b7448397b98212e0ed443 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Sun, 30 Dec 2018 00:47:10 +0000 Subject: [PATCH 047/147] Increased UI test timeouts --- tests/browser/nightwatch.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/browser/nightwatch.js b/tests/browser/nightwatch.js index dd8401fe..52587d2f 100644 --- a/tests/browser/nightwatch.js +++ b/tests/browser/nightwatch.js @@ -69,7 +69,7 @@ module.exports = { .useXpath() .moveToElement(toHex, 10, 10) .useCss() - .waitForElementVisible(".popover-body", 500) + .waitForElementVisible(".popover-body", 1000) .doubleClick(); // Confirm that it has been added to the recipe @@ -87,14 +87,14 @@ module.exports = { // Check output browser .useCss() - .waitForElementNotVisible("#stale-indicator", 100) + .waitForElementNotVisible("#stale-indicator", 500) .expect.element("#output-text").to.have.value.that.equals("44 6f 6e 27 74 20 50 61 6e 69 63 2e"); // Clear recipe browser .useCss() .moveToElement(op, 10, 10) - .waitForElementNotPresent(".popover-body", 500) + .waitForElementNotPresent(".popover-body", 1000) .click("#clr-recipe") .waitForElementNotPresent(op); }, From 19b3dcf1c20018a1c2a4ff3eec2e1b566a1b099f Mon Sep 17 00:00:00 2001 From: n1474335 Date: Sun, 30 Dec 2018 01:07:26 +0000 Subject: [PATCH 048/147] Updated CHANGELOG --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90948ef6..7a71bb41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ 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.19.0] - 2018-12-30 +- UI test suite added to confirm that the app loads correctly in a reasonable time and that various operations from each module can be run [@n1474335] | [#458] + ### [8.18.0] - 2018-12-26 - 'Split Colour Channels' operation added [@artemisbot] | [#449] @@ -85,6 +88,7 @@ All major and minor version changes will be documented in this file. Details of +[8.19.0]: https://github.com/gchq/CyberChef/releases/tag/v8.19.0 [8.18.0]: https://github.com/gchq/CyberChef/releases/tag/v8.18.0 [8.17.0]: https://github.com/gchq/CyberChef/releases/tag/v8.17.0 [8.16.0]: https://github.com/gchq/CyberChef/releases/tag/v8.16.0 @@ -155,3 +159,4 @@ All major and minor version changes will be documented in this file. Details of [#446]: https://github.com/gchq/CyberChef/pull/446 [#448]: https://github.com/gchq/CyberChef/pull/448 [#449]: https://github.com/gchq/CyberChef/pull/449 +[#458]: https://github.com/gchq/CyberChef/pull/458 From 0cea56dc62800d652cc2df205e8408ea68ce2bee Mon Sep 17 00:00:00 2001 From: n1474335 Date: Sun, 30 Dec 2018 01:07:54 +0000 Subject: [PATCH 049/147] 8.19.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d5d75248..e6717c1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.18.1", + "version": "8.19.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 8353acd5..eb1cdfcf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.18.1", + "version": "8.19.0", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 688c2d0df5beb90baa6c1d82f84fdabaae601f27 Mon Sep 17 00:00:00 2001 From: Edward Wall <33730573+edwardwall@users.noreply.github.com> Date: Sun, 30 Dec 2018 03:15:07 +0000 Subject: [PATCH 050/147] Update ParseX509Certificate.mjs --- src/core/operations/ParseX509Certificate.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/operations/ParseX509Certificate.mjs b/src/core/operations/ParseX509Certificate.mjs index 8af7040e..0b6da703 100644 --- a/src/core/operations/ParseX509Certificate.mjs +++ b/src/core/operations/ParseX509Certificate.mjs @@ -181,8 +181,8 @@ class ParseX509Certificate extends Operation { Serial number: ${new r.BigInteger(sn, 16).toString()} (0x${sn}) Algorithm ID: ${cert.getSignatureAlgorithmField()} Validity - Not Before: ${nbDate} (dd-mm-yy hh:mm:ss) (${cert.getNotBefore()}) - Not After: ${naDate} (dd-mm-yy hh:mm:ss) (${cert.getNotAfter()}) + Not Before: ${nbDate} (dd-mm-yyyy hh:mm:ss) (${cert.getNotBefore()}) + Not After: ${naDate} (dd-mm-yyyy hh:mm:ss) (${cert.getNotAfter()}) Issuer ${issuerStr} Subject @@ -208,6 +208,7 @@ ${extensions}`; function formatDate (dateStr) { return dateStr[4] + dateStr[5] + "/" + dateStr[2] + dateStr[3] + "/" + + (dateStr[0] < "5" ? "20" : "19") + dateStr[0] + dateStr[1] + " " + dateStr[6] + dateStr[7] + ":" + dateStr[8] + dateStr[9] + ":" + From 32aea6b86cb982d4f901197c162ce10fe90ee644 Mon Sep 17 00:00:00 2001 From: Spencer Walden Date: Sun, 30 Dec 2018 03:20:24 -0800 Subject: [PATCH 051/147] Adds 'To Case Insensitive Regex' operation --- .../operations/ToCaseInsensitiveRegex.mjs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/core/operations/ToCaseInsensitiveRegex.mjs diff --git a/src/core/operations/ToCaseInsensitiveRegex.mjs b/src/core/operations/ToCaseInsensitiveRegex.mjs new file mode 100644 index 00000000..32fb96c7 --- /dev/null +++ b/src/core/operations/ToCaseInsensitiveRegex.mjs @@ -0,0 +1,39 @@ +/** + * @author masq [github.cyberchef@masq.cc] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; + +/** + * To Case Insensitive Regex operation + */ +class ToCaseInsensitiveRegex extends Operation { + + /** + * ToCaseInsensitiveRegex constructor + */ + constructor() { + super(); + + this.name = "To Case Insensitive Regex"; + this.module = "Default"; + this.description = "Converts a case-sensitive regex string into a case-insensitive regex string in case /i flag is unavailable to you."; + this.infoURL = "https://wikipedia.org/wiki/Regular_expression"; + this.inputType = "string"; + this.outputType = "string"; + this.args = []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + return input.replace(/[a-z]/ig, m => `[${m.toLowerCase()}${m.toUpperCase()}]`); + } +} + +export default ToCaseInsensitiveRegex; From 3c16b839b6fdbfe1d95cd0df6337f94d6f7f31c6 Mon Sep 17 00:00:00 2001 From: Spencer Walden Date: Sun, 30 Dec 2018 03:20:44 -0800 Subject: [PATCH 052/147] Adds 'From Case Insensitive Regex' operation --- .../operations/FromCaseInsensitiveRegex.mjs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/core/operations/FromCaseInsensitiveRegex.mjs diff --git a/src/core/operations/FromCaseInsensitiveRegex.mjs b/src/core/operations/FromCaseInsensitiveRegex.mjs new file mode 100644 index 00000000..2448c5e5 --- /dev/null +++ b/src/core/operations/FromCaseInsensitiveRegex.mjs @@ -0,0 +1,39 @@ +/** + * @author masq [github.cyberchef@masq.cc] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; + +/** + * From Case Insensitive Regex operation + */ +class FromCaseInsensitiveRegex extends Operation { + + /** + * FromCaseInsensitiveRegex constructor + */ + constructor() { + super(); + + this.name = "From Case Insensitive Regex"; + this.module = "Default"; + this.description = "Converts a case-insensitive regex string to a case sensitive regex string (no guarantee on it being the proper original casing) in case /i wasn't available at the time but now is, or you need it to be case-sensitive again."; + this.infoURL = "https://wikipedia.org/wiki/Regular_expression"; + this.inputType = "string"; + this.outputType = "string"; + this.args = []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + return input.replace(/\[[a-z]{2}\]/ig, m => m[1].toUpperCase() === m[2].toUpperCase() ? m[1] : m); + } +} + +export default FromCaseInsensitiveRegex; From b750006cf0bef9b8609d30ffb445281150b0837a Mon Sep 17 00:00:00 2001 From: Spencer Walden Date: Sun, 30 Dec 2018 03:21:19 -0800 Subject: [PATCH 053/147] Adds tests for 'To/From Case Insensitive Regex' operations --- .../operations/ToFromInsensitiveRegex.mjs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 test/tests/operations/ToFromInsensitiveRegex.mjs diff --git a/test/tests/operations/ToFromInsensitiveRegex.mjs b/test/tests/operations/ToFromInsensitiveRegex.mjs new file mode 100644 index 00000000..13c24e44 --- /dev/null +++ b/test/tests/operations/ToFromInsensitiveRegex.mjs @@ -0,0 +1,56 @@ +/** + * To/From Case Insensitive Regex tests. + * + * @author masq [github.cyberchef@masq.cc] + * + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +import TestRegister from "../../TestRegister"; + +TestRegister.addTests([ + { + name: "To Case Insensitive Regex: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "To Case Insensitive Regex", + args: [], + }, + ], + }, + { + name: "From Case Insensitive Regex: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "From Case Insensitive Regex", + args: [], + }, + ], + }, + { + name: "To Case Insensitive Regex: simple test", + input: "S0meth!ng", + expectedOutput: "[sS]0[mM][eE][tT][hH]![nN][gG]", + recipeConfig: [ + { + op: "To Case Insensitive Regex", + args: [], + }, + ], + }, + { + name: "From Case Insensitive Regex: simple test", + input: "[sS]0[mM][eE][tT][hH]![nN][Gg] [wr][On][g]?", + expectedOutput: "s0meth!nG [wr][On][g]?", + recipeConfig: [ + { + op: "From Case Insensitive Regex", + args: [], + }, + ], + }, +]); From 1d04b649e00f81220e8ad7c00744817d6aa87acc Mon Sep 17 00:00:00 2001 From: Spencer Walden Date: Sun, 30 Dec 2018 03:21:52 -0800 Subject: [PATCH 054/147] Adds 'To/From Case Insensitive Regex' operations under 'Utils' --- src/core/config/Categories.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index a93149fe..c2dad458 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -189,6 +189,8 @@ "Remove null bytes", "To Upper case", "To Lower case", + "To Case Insensitive Regex", + "From Case Insensitive Regex", "Add line numbers", "Remove line numbers", "To Table", From 126ad585c03f238769fc085fc151f56fd6fe1d2c Mon Sep 17 00:00:00 2001 From: Spencer Walden Date: Sun, 30 Dec 2018 03:22:23 -0800 Subject: [PATCH 055/147] Registers tests for 'To/From Case Insensitive Regex' operations --- test/index.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/index.mjs b/test/index.mjs index ff13fe3b..3900490a 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -82,6 +82,7 @@ import "./tests/operations/TranslateDateTimeFormat"; import "./tests/operations/Magic"; import "./tests/operations/ParseTLV"; import "./tests/operations/Media"; +import "./tests/operations/ToFromInsensitiveRegex"; let allTestsPassing = true; const testStatusCounts = { From 40acf751a8a610e247a743dfc91511caa5b1c3b3 Mon Sep 17 00:00:00 2001 From: Edward Wall <33730573+edwardwall@users.noreply.github.com> Date: Sun, 30 Dec 2018 16:46:18 +0000 Subject: [PATCH 056/147] Update to understand Generalized / UTC Time Future proofing for when certificates with dates after 2049 begin being issued. These certificates' dates will be in Generalized Time not UTC Time as per RFC 5280 --- src/core/operations/ParseX509Certificate.mjs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/core/operations/ParseX509Certificate.mjs b/src/core/operations/ParseX509Certificate.mjs index 0b6da703..31e5e359 100644 --- a/src/core/operations/ParseX509Certificate.mjs +++ b/src/core/operations/ParseX509Certificate.mjs @@ -206,13 +206,19 @@ ${extensions}`; * @returns {string} */ function formatDate (dateStr) { - return dateStr[4] + dateStr[5] + "/" + - dateStr[2] + dateStr[3] + "/" + - (dateStr[0] < "5" ? "20" : "19") + - dateStr[0] + dateStr[1] + " " + - dateStr[6] + dateStr[7] + ":" + + if (dateStr.length === 13) { // UTC Time + if (dateStr[0] < "5") { + dateStr = "20" + dateStr; + } else { + dateStr = "19" + dateStr; + } + } + return dateStr[6] + dateStr[7] + "/" + + dateStr[4] + dateStr[5] + "/" + + dateStr[0] + dateStr[1] + dateStr[2] + dateStr[3] + " " + dateStr[8] + dateStr[9] + ":" + - dateStr[10] + dateStr[11]; + dateStr[10] + dateStr[11] + ":" + + dateStr[12] + dateStr[13]; } export default ParseX509Certificate; From 050ab03448f7badc4e7a46d863c85bbd6c80d8fc Mon Sep 17 00:00:00 2001 From: Edward Wall <33730573+edwardwall@users.noreply.github.com> Date: Sun, 30 Dec 2018 17:06:48 +0000 Subject: [PATCH 057/147] Simplify to improve readability --- src/core/operations/ParseX509Certificate.mjs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/core/operations/ParseX509Certificate.mjs b/src/core/operations/ParseX509Certificate.mjs index 31e5e359..a57f661e 100644 --- a/src/core/operations/ParseX509Certificate.mjs +++ b/src/core/operations/ParseX509Certificate.mjs @@ -207,11 +207,7 @@ ${extensions}`; */ function formatDate (dateStr) { if (dateStr.length === 13) { // UTC Time - if (dateStr[0] < "5") { - dateStr = "20" + dateStr; - } else { - dateStr = "19" + dateStr; - } + dateStr = (dateStr[0] < "5" ? "20" : "19") + dateStr; } return dateStr[6] + dateStr[7] + "/" + dateStr[4] + dateStr[5] + "/" + From d469fb9c58834164fee8e985a5f0dfe138bcf962 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 1 Jan 2019 19:19:07 +0000 Subject: [PATCH 058/147] Updated dependencies --- package-lock.json | 1550 ++++++++++++---------- package.json | 44 +- src/core/operations/BSONDeserialise.mjs | 4 +- src/core/operations/BSONSerialise.mjs | 4 +- src/core/operations/ToMessagePack.mjs | 4 +- src/web/stylesheets/layout/_controls.css | 2 +- 6 files changed, 913 insertions(+), 695 deletions(-) diff --git a/package-lock.json b/package-lock.json index e6717c1e..da318772 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,18 +14,18 @@ } }, "@babel/core": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.1.6.tgz", - "integrity": "sha512-Hz6PJT6e44iUNpAn8AoyAs6B3bl60g7MJQaI0rZEar6ECzh6+srYO1xlIdssio34mPaUtAb1y+XlkkSJzok3yw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.2.2.tgz", + "integrity": "sha512-59vB0RWt09cAct5EIe58+NzGP4TFSD3Bz//2/ELy3ZeTeKF6VTD1AXlH8BGGbCX0PuobZBsIzO7IAI9PH67eKw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.1.6", - "@babel/helpers": "^7.1.5", - "@babel/parser": "^7.1.6", - "@babel/template": "^7.1.2", - "@babel/traverse": "^7.1.6", - "@babel/types": "^7.1.6", + "@babel/generator": "^7.2.2", + "@babel/helpers": "^7.2.0", + "@babel/parser": "^7.2.2", + "@babel/template": "^7.2.2", + "@babel/traverse": "^7.2.2", + "@babel/types": "^7.2.2", "convert-source-map": "^1.1.0", "debug": "^4.1.0", "json5": "^2.1.0", @@ -36,9 +36,9 @@ }, "dependencies": { "debug": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", - "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { "ms": "^2.1.1" @@ -59,12 +59,12 @@ } }, "@babel/generator": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.1.6.tgz", - "integrity": "sha512-brwPBtVvdYdGxtenbQgfCdDPmtkmUBZPjUoK5SXJEBuHaA5BCubh9ly65fzXz7R6o5rA76Rs22ES8Z+HCc0YIQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.2.tgz", + "integrity": "sha512-I4o675J/iS8k+P38dvJ3IBGqObLXyQLTxtrR4u9cSUJOURvafeEWb/pFMOTwtNrmq73mJzyF6ueTbO1BtN0Zeg==", "dev": true, "requires": { - "@babel/types": "^7.1.6", + "@babel/types": "^7.2.2", "jsesc": "^2.5.1", "lodash": "^4.17.10", "source-map": "^0.5.0", @@ -178,16 +178,16 @@ } }, "@babel/helper-module-transforms": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.1.0.tgz", - "integrity": "sha512-0JZRd2yhawo79Rcm4w0LwSMILFmFXjugG3yqf+P/UsKsRS1mJCmMwwlHDlMg7Avr9LrvSpp4ZSULO9r8jpCzcw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.2.2.tgz", + "integrity": "sha512-YRD7I6Wsv+IHuTPkAmAS4HhY0dkPobgLftHp0cRGZSdrRvmZY8rFvae/GVu3bD00qscuvK3WPHB3YdNpBXUqrA==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", "@babel/helper-simple-access": "^7.1.0", "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0", + "@babel/template": "^7.2.2", + "@babel/types": "^7.2.2", "lodash": "^4.17.10" } }, @@ -229,14 +229,14 @@ } }, "@babel/helper-replace-supers": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.1.0.tgz", - "integrity": "sha512-BvcDWYZRWVuDeXTYZWxekQNO5D4kO55aArwZOTFXw6rlLQA8ZaDicJR1sO47h+HrnCiDFiww0fSPV0d713KBGQ==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.2.3.tgz", + "integrity": "sha512-GyieIznGUfPXPWu0yLS6U55Mz67AZD9cUk0BfirOWlPrXlBcan9Gz+vHGz+cPfuoweZSnPzPIm67VtQM0OWZbA==", "dev": true, "requires": { "@babel/helper-member-expression-to-functions": "^7.0.0", "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/traverse": "^7.1.0", + "@babel/traverse": "^7.2.3", "@babel/types": "^7.0.0" } }, @@ -260,26 +260,26 @@ } }, "@babel/helper-wrap-function": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.1.0.tgz", - "integrity": "sha512-R6HU3dete+rwsdAfrOzTlE9Mcpk4RjU3aX3gi9grtmugQY0u79X7eogUvfXA5sI81Mfq1cn6AgxihfN33STjJA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", + "integrity": "sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==", "dev": true, "requires": { "@babel/helper-function-name": "^7.1.0", "@babel/template": "^7.1.0", "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/types": "^7.2.0" } }, "@babel/helpers": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.1.5.tgz", - "integrity": "sha512-2jkcdL02ywNBry1YNFAH/fViq4fXG0vdckHqeJk+75fpQ2OH+Az6076tX/M0835zA45E0Cqa6pV5Kiv9YOqjEg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.2.0.tgz", + "integrity": "sha512-Fr07N+ea0dMcMN8nFpuK6dUIT7/ivt9yKQdEEnjVS83tG2pHwPi03gYmk/tyuwONnZ+sY+GFFPlWGgCtW1hF9A==", "dev": true, "requires": { "@babel/template": "^7.1.2", "@babel/traverse": "^7.1.5", - "@babel/types": "^7.1.5" + "@babel/types": "^7.2.0" } }, "@babel/highlight": { @@ -331,56 +331,56 @@ } }, "@babel/parser": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.6.tgz", - "integrity": "sha512-dWP6LJm9nKT6ALaa+bnL247GHHMWir3vSlZ2+IHgHgktZQx0L3Uvq2uAWcuzIe+fujRsYWBW2q622C5UvGK9iQ==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.2.3.tgz", + "integrity": "sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.1.0.tgz", - "integrity": "sha512-Fq803F3Jcxo20MXUSDdmZZXrPe6BWyGcWBPPNB/M7WaUYESKDeKMOGIxEzQOjGSmW/NWb6UaPZrtTB2ekhB/ew==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz", + "integrity": "sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/helper-remap-async-to-generator": "^7.1.0", - "@babel/plugin-syntax-async-generators": "^7.0.0" + "@babel/plugin-syntax-async-generators": "^7.2.0" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.0.0.tgz", - "integrity": "sha512-kfVdUkIAGJIVmHmtS/40i/fg/AGnw/rsZBCaapY5yjeO5RA9m165Xbw9KMOu2nqXP5dTFjEjHdfNdoVcHv133Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", + "integrity": "sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-json-strings": "^7.0.0" + "@babel/plugin-syntax-json-strings": "^7.2.0" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.0.0.tgz", - "integrity": "sha512-14fhfoPcNu7itSen7Py1iGN0gEm87hX/B+8nZPqkdmANyyYWYMY2pjA3r8WXbWVKMzfnSNS0xY8GVS0IjXi/iw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.2.0.tgz", + "integrity": "sha512-1L5mWLSvR76XYUQJXkd/EEQgjq8HHRP6lQuZTTg0VA4tTGPpGemmCdAfQIz1rzEuWAm+ecP8PyyEm30jC1eQCg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.0.0" + "@babel/plugin-syntax-object-rest-spread": "^7.2.0" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.0.0.tgz", - "integrity": "sha512-JPqAvLG1s13B/AuoBjdBYvn38RqW6n1TzrQO839/sIpqLpbnXKacsAgpZHzLD83Sm8SDXMkkrAvEnJ25+0yIpw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.0.0" + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.0.0.tgz", - "integrity": "sha512-tM3icA6GhC3ch2SkmSxv7J/hCWKISzwycub6eGsDrFDgukD4dZ/I+x81XgW0YslS6mzNuQ1Cbzh5osjIMgepPQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.2.0.tgz", + "integrity": "sha512-LvRVYb7kikuOtIoUeWTkOxQEV1kYvL5B6U3iWEGCzPNRus1MzJweFqORTj+0jkxozkTSYNJozPOddxmqdqsRpw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -389,54 +389,54 @@ } }, "@babel/plugin-syntax-async-generators": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.0.0.tgz", - "integrity": "sha512-im7ged00ddGKAjcZgewXmp1vxSZQQywuQXe2B1A7kajjZmDeY/ekMPmWr9zJgveSaQH0k7BcGrojQhcK06l0zA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", + "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-syntax-json-strings": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.0.0.tgz", - "integrity": "sha512-UlSfNydC+XLj4bw7ijpldc1uZ/HB84vw+U6BTuqMdIEmz/LDe63w/GHtpQMdXWdqQZFeAI9PjnHe/vDhwirhKA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", + "integrity": "sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-syntax-object-rest-spread": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0.tgz", - "integrity": "sha512-5A0n4p6bIiVe5OvQPxBnesezsgFJdHhSs3uFSvaPdMqtsovajLZ+G2vZyvNe10EzJBWWo3AcHGKhAFUxqwp2dw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", + "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.0.0.tgz", - "integrity": "sha512-Wc+HVvwjcq5qBg1w5RG9o9RVzmCaAg/Vp0erHCKpAYV8La6I94o4GQAmFYNmkzoMO6gzoOSulpKeSSz6mPEoZw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0.tgz", - "integrity": "sha512-2EZDBl1WIO/q4DIkIp4s86sdp4ZifL51MoIviLY/gG/mLSuOIEg7J8o6mhbxOTvUJkaN50n+8u41FVsr5KLy/w==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", + "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.1.0.tgz", - "integrity": "sha512-rNmcmoQ78IrvNCIt/R9U+cixUHeYAzgusTFgIAv+wQb9HJU4szhpDD6e5GCACmj/JP5KxuCwM96bX3L9v4ZN/g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.2.0.tgz", + "integrity": "sha512-CEHzg4g5UraReozI9D4fblBYABs7IM6UerAVG7EJVrTLC5keh00aEuLUT+O40+mJCEzaXkYfTCUKIyeDfMOFFQ==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", @@ -445,18 +445,18 @@ } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.0.0.tgz", - "integrity": "sha512-AOBiyUp7vYTqz2Jibe1UaAWL0Hl9JUXEgjFvvvcSc9MVDItv46ViXFw2F7SVt1B5k+KWjl44eeXOAk3UDEaJjQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz", + "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.1.5.tgz", - "integrity": "sha512-jlYcDrz+5ayWC7mxgpn1Wj8zj0mmjCT2w0mPIMSwO926eXBRxpEgoN/uQVRBfjtr8ayjcmS+xk2G1jaP8JjMJQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.2.0.tgz", + "integrity": "sha512-vDTgf19ZEV6mx35yiPJe4fS02mPQUUcBNwWQSZFXSzTSbsJFQvHt7DqyS3LK8oOWALFOsJ+8bbqBgkirZteD5Q==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -464,9 +464,9 @@ } }, "@babel/plugin-transform-classes": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.1.0.tgz", - "integrity": "sha512-rNaqoD+4OCBZjM7VaskladgqnZ1LO6o2UxuWSDzljzW21pN1KXkB7BstAVweZdxQkHAujps5QMNOTWesBciKFg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.2.2.tgz", + "integrity": "sha512-gEZvgTy1VtcDOaQty1l10T3jQmJKlNVxLDCs+3rCVPr6nMkODLELxViq5X9l+rfxbie3XrfrMCYYY6eX3aOcOQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.0.0", @@ -488,27 +488,27 @@ } }, "@babel/plugin-transform-computed-properties": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.0.0.tgz", - "integrity": "sha512-ubouZdChNAv4AAWAgU7QKbB93NU5sHwInEWfp+/OzJKA02E6Woh9RVoX4sZrbRwtybky/d7baTUqwFx+HgbvMA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz", + "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-destructuring": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.1.3.tgz", - "integrity": "sha512-Mb9M4DGIOspH1ExHOUnn2UUXFOyVTiX84fXCd+6B5iWrQg/QMeeRmSwpZ9lnjYLSXtZwiw80ytVMr3zue0ucYw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.2.0.tgz", + "integrity": "sha512-coVO2Ayv7g0qdDbrNiadE4bU7lvCd9H539m2gMknyVjjMdwF/iCOM7R+E8PkntoqLkltO0rk+3axhpp/0v68VQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.0.0.tgz", - "integrity": "sha512-00THs8eJxOJUFVx1w8i1MBF4XH4PsAjKjQ1eqN/uCH3YKwP21GCKfrn6YZFZswbOk9+0cw1zGQPHVc1KBlSxig==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.2.0.tgz", + "integrity": "sha512-sKxnyHfizweTgKZf7XsXu/CNupKhzijptfTM+bozonIuyVrLWVUvYjE2bhuSBML8VQeMxq4Mm63Q9qvcvUcciQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -517,18 +517,18 @@ } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.0.0.tgz", - "integrity": "sha512-w2vfPkMqRkdxx+C71ATLJG30PpwtTpW7DDdLqYt2acXU7YjztzeWW2Jk1T6hKqCLYCcEA5UQM/+xTAm+QCSnuQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz", + "integrity": "sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.1.0.tgz", - "integrity": "sha512-uZt9kD1Pp/JubkukOGQml9tqAeI8NkE98oZnHZ2qHRElmeKCodbTZgOEUtujSCSLhHSBWbzNiFSDIMC4/RBTLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz", + "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==", "dev": true, "requires": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", @@ -536,18 +536,18 @@ } }, "@babel/plugin-transform-for-of": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.0.0.tgz", - "integrity": "sha512-TlxKecN20X2tt2UEr2LNE6aqA0oPeMT1Y3cgz8k4Dn1j5ObT8M3nl9aA37LLklx0PBZKETC9ZAf9n/6SujTuXA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.2.0.tgz", + "integrity": "sha512-Kz7Mt0SsV2tQk6jG5bBv5phVbkd0gd27SgYD4hH1aLMJRchM0dzHaXvrWhVZ+WxAlDoAKZ7Uy3jVTW2mKXQ1WQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.1.0.tgz", - "integrity": "sha512-VxOa1TMlFMtqPW2IDYZQaHsFrq/dDoIjgN098NowhexhZcz3UGlvPgZXuE1jEvNygyWyxRacqDpCZt+par1FNg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.2.0.tgz", + "integrity": "sha512-kWgksow9lHdvBC2Z4mxTsvc7YdY7w/V6B2vy9cTIPtLEE9NhwoWivaxdNM/S37elu5bqlLP/qOY906LukO9lkQ==", "dev": true, "requires": { "@babel/helper-function-name": "^7.1.0", @@ -555,18 +555,18 @@ } }, "@babel/plugin-transform-literals": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.0.0.tgz", - "integrity": "sha512-1NTDBWkeNXgpUcyoVFxbr9hS57EpZYXpje92zv0SUzjdu3enaRwF/l3cmyRnXLtIdyJASyiS6PtybK+CgKf7jA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz", + "integrity": "sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.1.0.tgz", - "integrity": "sha512-wt8P+xQ85rrnGNr2x1iV3DW32W8zrB6ctuBkYBbf5/ZzJY99Ob4MFgsZDFgczNU76iy9PWsy4EuxOliDjdKw6A==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz", + "integrity": "sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw==", "dev": true, "requires": { "@babel/helper-module-transforms": "^7.1.0", @@ -574,9 +574,9 @@ } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.1.0.tgz", - "integrity": "sha512-wtNwtMjn1XGwM0AXPspQgvmE6msSJP15CX2RVfpTSTNPLhKhaOjaIfBaVfj4iUZ/VrFSodcFedwtPg/NxwQlPA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.2.0.tgz", + "integrity": "sha512-V6y0uaUQrQPXUrmj+hgnks8va2L0zcZymeU7TtWEgdRLNkceafKXEduv7QzgQAE4lT+suwooG9dC7LFhdRAbVQ==", "dev": true, "requires": { "@babel/helper-module-transforms": "^7.1.0", @@ -585,9 +585,9 @@ } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.1.3.tgz", - "integrity": "sha512-PvTxgjxQAq4pvVUZF3mD5gEtVDuId8NtWkJsZLEJZMZAW3TvgQl1pmydLLN1bM8huHFVVU43lf0uvjQj9FRkKw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.2.0.tgz", + "integrity": "sha512-aYJwpAhoK9a+1+O625WIjvMY11wkB/ok0WClVwmeo3mCjcNRjt+/8gHWrB5i+00mUju0gWsBkQnPpdvQ7PImmQ==", "dev": true, "requires": { "@babel/helper-hoist-variables": "^7.0.0", @@ -595,9 +595,9 @@ } }, "@babel/plugin-transform-modules-umd": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.1.0.tgz", - "integrity": "sha512-enrRtn5TfRhMmbRwm7F8qOj0qEYByqUvTttPEGimcBH4CJHphjyK1Vg7sdU7JjeEmgSpM890IT/efS2nMHwYig==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz", + "integrity": "sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw==", "dev": true, "requires": { "@babel/helper-module-transforms": "^7.1.0", @@ -614,9 +614,9 @@ } }, "@babel/plugin-transform-object-super": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.1.0.tgz", - "integrity": "sha512-/O02Je1CRTSk2SSJaq0xjwQ8hG4zhZGNjE8psTsSNPXyLRCODv7/PBozqT5AmQMzp7MI3ndvMhGdqp9c96tTEw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz", + "integrity": "sha512-VMyhPYZISFZAqAPVkiYb7dUe2AsVi2/wCT5+wZdsNO31FojQJa9ns40hzZ6U9f50Jlq4w6qwzdBB2uwqZ00ebg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -624,9 +624,9 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.1.0.tgz", - "integrity": "sha512-vHV7oxkEJ8IHxTfRr3hNGzV446GAb+0hgbA7o/0Jd76s+YzccdWuTU296FOCOl/xweU4t/Ya4g41yWz80RFCRw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.2.0.tgz", + "integrity": "sha512-kB9+hhUidIgUoBQ0MsxMewhzr8i60nMa2KgeJKQWYrqQpqcBYtnpR+JgkadZVZoaEZ/eKu9mclFaVwhRpLNSzA==", "dev": true, "requires": { "@babel/helper-call-delegate": "^7.1.0", @@ -644,27 +644,27 @@ } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0.tgz", - "integrity": "sha512-g/99LI4vm5iOf5r1Gdxq5Xmu91zvjhEG5+yZDJW268AZELAu4J1EiFLnkSG3yuUsZyOipVOVUKoGPYwfsTymhw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz", + "integrity": "sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-spread": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.0.0.tgz", - "integrity": "sha512-L702YFy2EvirrR4shTj0g2xQp7aNwZoWNCkNu2mcoU0uyzMl0XRwDSwzB/xp6DSUFiBmEXuyAyEN16LsgVqGGQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz", + "integrity": "sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.0.0.tgz", - "integrity": "sha512-LFUToxiyS/WD+XEWpkx/XJBrUXKewSZpzX68s+yEOtIbdnsRjpryDw9U06gYc6klYEij/+KQVRnD3nz3AoKmjw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz", + "integrity": "sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -672,9 +672,9 @@ } }, "@babel/plugin-transform-template-literals": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.0.0.tgz", - "integrity": "sha512-vA6rkTCabRZu7Nbl9DfLZE1imj4tzdWcg5vtdQGvj+OH9itNNB6hxuRMHuIY8SGnEt1T9g5foqs9LnrHzsqEFg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.2.0.tgz", + "integrity": "sha512-FkPix00J9A/XWXv4VoKJBMeSkyY9x/TqIh76wzcdfl57RJJcf8CehQ08uwfhCDNtRQYtHQKBTwKZDEyjE13Lwg==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.0.0", @@ -682,18 +682,18 @@ } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.0.0.tgz", - "integrity": "sha512-1r1X5DO78WnaAIvs5uC48t41LLckxsYklJrZjNKcevyz83sF2l4RHbw29qrCPr/6ksFsdfRpT/ZgxNWHXRnffg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz", + "integrity": "sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.0.0.tgz", - "integrity": "sha512-uJBrJhBOEa3D033P95nPHu3nbFwFE9ZgXsfEitzoIXIwqAZWk7uXcg06yFKXz9FSxBH5ucgU/cYdX0IV8ldHKw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.2.0.tgz", + "integrity": "sha512-m48Y0lMhrbXEJnVUaYly29jRXbQ3ksxPrS1Tg8t+MHqzXhtBYAvI51euOBaoAlZLPHsieY9XPVMf80a5x0cPcA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -718,86 +718,86 @@ } }, "@babel/preset-env": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.1.6.tgz", - "integrity": "sha512-YIBfpJNQMBkb6MCkjz/A9J76SNCSuGVamOVBgoUkLzpJD/z8ghHi9I42LQ4pulVX68N/MmImz6ZTixt7Azgexw==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.2.3.tgz", + "integrity": "sha512-AuHzW7a9rbv5WXmvGaPX7wADxFkZIqKlbBh1dmZUQp4iwiPpkE/Qnrji6SC4UQCQzvWY/cpHET29eUhXS9cLPw==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-async-generator-functions": "^7.1.0", - "@babel/plugin-proposal-json-strings": "^7.0.0", - "@babel/plugin-proposal-object-rest-spread": "^7.0.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.0.0", - "@babel/plugin-syntax-async-generators": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.0.0", - "@babel/plugin-transform-arrow-functions": "^7.0.0", - "@babel/plugin-transform-async-to-generator": "^7.1.0", - "@babel/plugin-transform-block-scoped-functions": "^7.0.0", - "@babel/plugin-transform-block-scoping": "^7.1.5", - "@babel/plugin-transform-classes": "^7.1.0", - "@babel/plugin-transform-computed-properties": "^7.0.0", - "@babel/plugin-transform-destructuring": "^7.0.0", - "@babel/plugin-transform-dotall-regex": "^7.0.0", - "@babel/plugin-transform-duplicate-keys": "^7.0.0", - "@babel/plugin-transform-exponentiation-operator": "^7.1.0", - "@babel/plugin-transform-for-of": "^7.0.0", - "@babel/plugin-transform-function-name": "^7.1.0", - "@babel/plugin-transform-literals": "^7.0.0", - "@babel/plugin-transform-modules-amd": "^7.1.0", - "@babel/plugin-transform-modules-commonjs": "^7.1.0", - "@babel/plugin-transform-modules-systemjs": "^7.0.0", - "@babel/plugin-transform-modules-umd": "^7.1.0", + "@babel/plugin-proposal-async-generator-functions": "^7.2.0", + "@babel/plugin-proposal-json-strings": "^7.2.0", + "@babel/plugin-proposal-object-rest-spread": "^7.2.0", + "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.2.0", + "@babel/plugin-syntax-async-generators": "^7.2.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", + "@babel/plugin-transform-arrow-functions": "^7.2.0", + "@babel/plugin-transform-async-to-generator": "^7.2.0", + "@babel/plugin-transform-block-scoped-functions": "^7.2.0", + "@babel/plugin-transform-block-scoping": "^7.2.0", + "@babel/plugin-transform-classes": "^7.2.0", + "@babel/plugin-transform-computed-properties": "^7.2.0", + "@babel/plugin-transform-destructuring": "^7.2.0", + "@babel/plugin-transform-dotall-regex": "^7.2.0", + "@babel/plugin-transform-duplicate-keys": "^7.2.0", + "@babel/plugin-transform-exponentiation-operator": "^7.2.0", + "@babel/plugin-transform-for-of": "^7.2.0", + "@babel/plugin-transform-function-name": "^7.2.0", + "@babel/plugin-transform-literals": "^7.2.0", + "@babel/plugin-transform-modules-amd": "^7.2.0", + "@babel/plugin-transform-modules-commonjs": "^7.2.0", + "@babel/plugin-transform-modules-systemjs": "^7.2.0", + "@babel/plugin-transform-modules-umd": "^7.2.0", "@babel/plugin-transform-new-target": "^7.0.0", - "@babel/plugin-transform-object-super": "^7.1.0", - "@babel/plugin-transform-parameters": "^7.1.0", + "@babel/plugin-transform-object-super": "^7.2.0", + "@babel/plugin-transform-parameters": "^7.2.0", "@babel/plugin-transform-regenerator": "^7.0.0", - "@babel/plugin-transform-shorthand-properties": "^7.0.0", - "@babel/plugin-transform-spread": "^7.0.0", - "@babel/plugin-transform-sticky-regex": "^7.0.0", - "@babel/plugin-transform-template-literals": "^7.0.0", - "@babel/plugin-transform-typeof-symbol": "^7.0.0", - "@babel/plugin-transform-unicode-regex": "^7.0.0", - "browserslist": "^4.1.0", + "@babel/plugin-transform-shorthand-properties": "^7.2.0", + "@babel/plugin-transform-spread": "^7.2.0", + "@babel/plugin-transform-sticky-regex": "^7.2.0", + "@babel/plugin-transform-template-literals": "^7.2.0", + "@babel/plugin-transform-typeof-symbol": "^7.2.0", + "@babel/plugin-transform-unicode-regex": "^7.2.0", + "browserslist": "^4.3.4", "invariant": "^2.2.2", "js-levenshtein": "^1.1.3", "semver": "^5.3.0" } }, "@babel/template": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.1.2.tgz", - "integrity": "sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", + "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.1.2", - "@babel/types": "^7.1.2" + "@babel/parser": "^7.2.2", + "@babel/types": "^7.2.2" } }, "@babel/traverse": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.1.6.tgz", - "integrity": "sha512-CXedit6GpISz3sC2k2FsGCUpOhUqKdyL0lqNrImQojagnUMXf8hex4AxYFRuMkNGcvJX5QAFGzB5WJQmSv8SiQ==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz", + "integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.1.6", + "@babel/generator": "^7.2.2", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/parser": "^7.1.6", - "@babel/types": "^7.1.6", + "@babel/parser": "^7.2.3", + "@babel/types": "^7.2.2", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.10" }, "dependencies": { "debug": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", - "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { "ms": "^2.1.1" @@ -818,9 +818,9 @@ } }, "@babel/types": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.1.6.tgz", - "integrity": "sha512-DMiUzlY9DSjVsOylJssxLHSgj6tWM9PRFJOGW/RaOglVOK9nzTxoOMfTfRQXGUCUQ/HmlG2efwC+XqUEJ5ay4w==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.2.2.tgz", + "integrity": "sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -1429,9 +1429,9 @@ } }, "acorn-jsx": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.0.tgz", - "integrity": "sha512-XkB50fn0MURDyww9+UYL3c1yLbOBz0ZFvrdYlGB8l+Ije1oSC75qAqrzSPjYQbdnQUzhlUGNKuesryAv0gxZOg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", "dev": true }, "acorn-walk": { @@ -1490,9 +1490,9 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, "ansi-colors": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.1.tgz", - "integrity": "sha512-Xt+zb6nqgvV9SWAVp0EG3lRsHcbq5DDgqjPPz6pwgtj6RKz65zGXMNa82oJfOSBA/to6GmRP7Dr+6o+kbApTzQ==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", "dev": true }, "ansi-escapes": { @@ -1571,7 +1571,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -1626,9 +1626,9 @@ "dev": true }, "array-flatten": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz", - "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", "dev": true }, "array-union": { @@ -1733,6 +1733,12 @@ "integrity": "sha512-2mP3TwtkY/aTv5X3ZsMpNAbOnyoC/aMJwJSoaELPkHId0nSQgFcnU4dRW3isxiz7+zBexk0ym3WNVjMiQBnJSw==", "dev": true }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "async": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", @@ -1773,17 +1779,59 @@ "dev": true }, "autoprefixer": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.3.1.tgz", - "integrity": "sha512-DY9gOh8z3tnCbJ13JIWaeQsoYncTGdsrgCceBaQSIL4nvdrLxgbRSBPevg2XbX7u4QCSfLheSJEEIUUSlkbx6Q==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.3.tgz", + "integrity": "sha512-/XSnzDepRkAU//xLcXA/lUWxpsBuw0WiriAHOqnxkuCtzLhaz+fL4it4gp20BQ8n5SyLzK/FOc7A0+u/rti2FQ==", "dev": true, "requires": { - "browserslist": "^4.3.3", - "caniuse-lite": "^1.0.30000898", + "browserslist": "^4.3.6", + "caniuse-lite": "^1.0.30000921", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", - "postcss": "^7.0.5", + "postcss": "^7.0.6", "postcss-value-parser": "^3.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "postcss": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz", + "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.5.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "aws-sign2": { @@ -2032,6 +2080,12 @@ "tryer": "^1.0.0" } }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, "bignumber.js": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-8.0.1.tgz", @@ -2154,9 +2208,9 @@ "dev": true }, "bootstrap": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.1.3.tgz", - "integrity": "sha512-rDFIzgXcof0jDyjNosjv4Sno77X4KuPeFxG2XZZv1/Kc8DRVGVADdoQyyOVDwPqL36DDmtCQbrpMCqvpPLJQ0w==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.2.1.tgz", + "integrity": "sha512-tt/7vIv3Gm2mnd/WeDx36nfGGHleil0Wg8IeB7eMrVkY0fZ5iTaBisSh8oNANc2IBsCc6vCgCNTIM/IEN0+50Q==", "dev": true }, "bootstrap-colorpicker": { @@ -2302,20 +2356,35 @@ } }, "browserslist": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.3.4.tgz", - "integrity": "sha512-u5iz+ijIMUlmV8blX82VGFrB9ecnUg5qEt55CMZ/YJEhha+d8qpBfOFuutJ6F/VKRXjZoD33b6uvarpPxcl3RA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.3.6.tgz", + "integrity": "sha512-kMGKs4BTzRWviZ8yru18xBpx+CyHG9eqgRbj9XbE3IMgtczf4aiA0Y1YCpVdvUieKGZ03kolSPXqTcscBCb9qw==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000899", - "electron-to-chromium": "^1.3.82", - "node-releases": "^1.0.1" + "caniuse-lite": "^1.0.30000921", + "electron-to-chromium": "^1.3.92", + "node-releases": "^1.1.1" } }, "bson": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/bson/-/bson-3.0.2.tgz", - "integrity": "sha512-HrDzr7y/ZkgyEVancPVDmfbaD8j81GzSNr6h6yUd/yZfavkrlrqI8aUZMCHrhyMoCW2/I+vEJDat1xDWRwVR6A==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.0.1.tgz", + "integrity": "sha512-Q+E5edAc2DnSb77xcBJga0iJDyZlhkKRhWxKdPJcT3UK6nC6BtmMJGpkt+99bGht3HIhXHu7mxi5FLBgQAj5MA==", + "requires": { + "buffer": "^5.1.0", + "long": "^4.0.0" + }, + "dependencies": { + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + } + } }, "buffer": { "version": "4.9.1", @@ -2388,31 +2457,47 @@ "integrity": "sha1-sC2wB+83vrzCk4Skssb08PTHlsk=" }, "cacache": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", - "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", "dev": true, "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", "mkdirp": "^0.5.1", "move-concurrently": "^1.0.1", "promise-inflight": "^1.0.1", "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", "y18n": "^4.0.0" }, "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, "y18n": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true } } }, @@ -2475,9 +2560,9 @@ } }, "caniuse-lite": { - "version": "1.0.30000907", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000907.tgz", - "integrity": "sha512-No5sQ/OB2Nmka8MNOOM6nJx+Hxt6MQ6h7t7kgJFu9oTuwjykyKRSBP/+i/QAyFHxeHB+ddE0Da1CG5ihx9oehQ==", + "version": "1.0.30000925", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000925.tgz", + "integrity": "sha512-zcYupoUxtW46rOikuDF7vfL9N1Qe9ZuUBTz3n3q8fFsoJIs/h9UN6Vg/0QpjsmvImXw9mVc3g+ZBfqvUz/iALA==", "dev": true }, "caseless": { @@ -2694,7 +2779,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -2761,9 +2846,9 @@ "dev": true }, "colors": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.2.tgz", - "integrity": "sha512-rhP0JSBGYvpcNQj4s5AdShMeE5ahMop96cTeDl/v9qQQm2fYClE2QXZRi8wLzc+GmXSxdIqqbOIAhyObEXDbfQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", "dev": true }, "combined-stream": { @@ -3116,23 +3201,21 @@ "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=" }, "css-loader": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.1.tgz", - "integrity": "sha512-+ZHAZm/yqvJ2kDtPne3uX0C+Vr3Zn5jFn2N4HywtS5ujwvsVkyg0VArEXpl3BgczDA8anieki1FIzhchX4yrDw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.0.tgz", + "integrity": "sha512-MoOu+CStsGrSt5K2OeZ89q3Snf+IkxRfAIt9aAKg4piioTrhtP1iEFPu+OVn3Ohz24FO6L+rw9UJxBILiSBw5Q==", "dev": true, "requires": { - "babel-code-frame": "^6.26.0", - "css-selector-tokenizer": "^0.7.0", - "icss-utils": "^2.1.0", - "loader-utils": "^1.0.2", + "icss-utils": "^4.0.0", + "loader-utils": "^1.2.1", "lodash": "^4.17.11", - "postcss": "^6.0.23", - "postcss-modules-extract-imports": "^1.2.0", - "postcss-modules-local-by-default": "^1.2.0", - "postcss-modules-scope": "^1.1.0", - "postcss-modules-values": "^1.3.0", + "postcss": "^7.0.6", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^2.0.3", + "postcss-modules-scope": "^2.0.0", + "postcss-modules-values": "^2.0.0", "postcss-value-parser": "^3.3.0", - "source-list-map": "^2.0.0" + "schema-utils": "^1.0.0" }, "dependencies": { "ansi-styles": { @@ -3155,15 +3238,46 @@ "supports-color": "^5.3.0" } }, + "json5": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz", + "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==", "dev": true, "requires": { "chalk": "^2.4.1", "source-map": "^0.6.1", - "supports-color": "^5.4.0" + "supports-color": "^5.5.0" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" } }, "supports-color": { @@ -3179,7 +3293,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -3208,7 +3322,7 @@ }, "regexpu-core": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", "dev": true, "requires": { @@ -3225,7 +3339,7 @@ }, "regjsparser": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "resolved": "http://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { @@ -3547,7 +3661,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -3611,7 +3725,7 @@ "dependencies": { "domelementtype": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", "dev": true }, @@ -3762,9 +3876,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.84", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.84.tgz", - "integrity": "sha512-IYhbzJYOopiTaNWMBp7RjbecUBsbnbDneOP86f3qvS0G0xfzwNSvMJpTrvi5/Y1gU7tg2NAgeg8a8rCYvW9Whw==", + "version": "1.3.96", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.96.tgz", + "integrity": "sha512-ZUXBUyGLeoJxp4Nt6G/GjBRLnyz8IKQGexZ2ndWaoegThgMGFO1tdDYID5gBV32/1S83osjJHyfzvanE/8HY4Q==", "dev": true }, "elliptic": { @@ -3816,7 +3930,7 @@ }, "entities": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/entities/-/entities-1.0.0.tgz", "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", "dev": true }, @@ -3948,9 +4062,9 @@ } }, "eslint": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.9.0.tgz", - "integrity": "sha512-g4KWpPdqN0nth+goDNICNXGfJF7nNnepthp46CAlJoJtC5K/cLu3NgCM3AHu1CkJ5Hzt9V0Y0PBAO6Ay/gGb+w==", + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.11.1.tgz", + "integrity": "sha512-gOKhM8JwlFOc2acbOrkYR05NW8M6DCMSvfcJiBB5NDxRE1gv8kbvxKaC9u69e6ZGEMWXcswA/7eKR229cEIpvg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -3962,7 +4076,7 @@ "eslint-scope": "^4.0.0", "eslint-utils": "^1.3.1", "eslint-visitor-keys": "^1.0.0", - "espree": "^4.0.0", + "espree": "^5.0.0", "esquery": "^1.0.1", "esutils": "^2.0.2", "file-entry-cache": "^2.0.0", @@ -3972,7 +4086,6 @@ "ignore": "^4.0.6", "imurmurhash": "^0.1.4", "inquirer": "^6.1.0", - "is-resolvable": "^1.1.0", "js-yaml": "^3.12.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", @@ -4020,9 +4133,9 @@ } }, "debug": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", - "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { "ms": "^2.1.1" @@ -4041,9 +4154,9 @@ "dev": true }, "progress": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz", - "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, "strip-ansi": { @@ -4158,7 +4271,7 @@ }, "source-map": { "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", "requires": { "amdefine": ">=0.0.4" @@ -4172,9 +4285,9 @@ } }, "espree": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", - "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.0.tgz", + "integrity": "sha512-1MpUfwsdS9MMoN7ZXqAr9e9UKdVHDcvrJpyx7mm1WuQlx/ygErEQBzgi5Nh5qBHIoYweprhtMkTCb9GhcAIcsA==", "dev": true, "requires": { "acorn": "^6.0.2", @@ -4240,7 +4353,7 @@ }, "eventemitter2": { "version": "0.4.14", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "resolved": "http://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", "dev": true }, @@ -4252,7 +4365,7 @@ }, "events": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "dev": true }, @@ -4348,7 +4461,7 @@ "dependencies": { "source-map": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", + "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", "integrity": "sha1-D+llA6yGpa213mP05BKuSHLNvoY=", "dev": true } @@ -4580,6 +4693,12 @@ "pend": "~1.2.0" } }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "dev": true + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -4600,9 +4719,9 @@ } }, "file-loader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-2.0.0.tgz", - "integrity": "sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", + "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", "dev": true, "requires": { "loader-utils": "^1.0.2", @@ -4710,7 +4829,7 @@ }, "findup-sync": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", "dev": true, "requires": { @@ -4733,14 +4852,14 @@ } }, "flat-cache": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.2.tgz", - "integrity": "sha512-KByBY8c98sLUAGpnmjEdWTrtrLZRtZdwds+kAL/ciFXTCb7AZgqKsAnVnYFQj1hxepwO8JKN/8AsRWwLq+RK0A==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", "dev": true, "requires": { "circular-json": "^0.3.1", - "del": "^3.0.0", "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", "write": "^0.2.1" } }, @@ -4777,7 +4896,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -4900,7 +5019,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -4911,7 +5030,7 @@ }, "fs-extra": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", "dev": true, "requires": { @@ -5541,7 +5660,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -5575,7 +5694,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -5704,7 +5823,7 @@ }, "globby": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { @@ -5717,7 +5836,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -5794,7 +5913,7 @@ }, "grunt-cli": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", "dev": true, "requires": { @@ -5816,7 +5935,7 @@ }, "resolve": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true } @@ -5842,7 +5961,7 @@ "dependencies": { "shelljs": { "version": "0.5.3", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", + "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", "integrity": "sha1-xUmCuZbHbvDB5rWfvcWCX1txMRM=", "dev": true } @@ -5907,7 +6026,7 @@ }, "grunt-contrib-jshint": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", "integrity": "sha1-Np2QmyWTxA6L55lAshNAhQx5Oaw=", "dev": true, "requires": { @@ -6097,9 +6216,9 @@ } }, "handle-thing": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", - "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", + "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", "dev": true }, "har-schema": { @@ -6196,9 +6315,9 @@ } }, "hash.js": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", - "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -6290,7 +6409,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -6331,7 +6450,7 @@ }, "html-webpack-plugin": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "resolved": "http://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", "dev": true, "requires": { @@ -6387,7 +6506,7 @@ }, "htmlparser2": { "version": "3.8.3", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", "dev": true, "requires": { @@ -6406,7 +6525,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -6456,7 +6575,7 @@ }, "http-proxy-middleware": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { @@ -6550,54 +6669,12 @@ "dev": true }, "icss-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", - "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.0.0.tgz", + "integrity": "sha512-bA/xGiwWM17qjllIs9X/y0EjsB7e0AV08F3OL8UPsoNkNRibIuu8f1eKTnQ8QO1DteKKTxTUAn+IEWUToIwGOA==", "dev": true, "requires": { - "postcss": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "postcss": "^7.0.5" } }, "ieee754": { @@ -6679,9 +6756,9 @@ } }, "p-limit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", - "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -6783,9 +6860,9 @@ } }, "inquirer": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", - "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz", + "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==", "dev": true, "requires": { "ansi-escapes": "^3.0.0", @@ -6799,14 +6876,14 @@ "run-async": "^2.2.0", "rxjs": "^6.1.0", "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", + "strip-ansi": "^5.0.0", "through": "^2.3.6" }, "dependencies": { "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz", + "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==", "dev": true }, "ansi-styles": { @@ -6830,12 +6907,12 @@ } }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz", + "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^4.0.0" } }, "supports-color": { @@ -6893,7 +6970,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -6934,7 +7011,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -6948,7 +7025,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { @@ -7115,12 +7192,6 @@ "has": "^1.0.1" } }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -7270,9 +7341,9 @@ "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" }, "js-base64": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.9.tgz", - "integrity": "sha512-xcinL3AuDJk7VSzsHgb9DvvIXayBbadtMZ4HFPx8rUszbW1MuNMlwYVC4zzCZ6e1sqZpnNS5ZFYOhXqA39T7LQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.0.tgz", + "integrity": "sha512-wlEBIZ5LP8usDylWbDNhKPEFVFdI5hCHpnVoT/Ysvoi/PRhJENm/Rlh9TvjYB38HFfKZN7OzEbRjmjvLkFw11g==", "dev": true }, "js-crc": { @@ -7358,7 +7429,7 @@ }, "underscore": { "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", "dev": true } @@ -7501,7 +7572,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -7612,7 +7683,7 @@ }, "kew": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", + "resolved": "http://registry.npmjs.org/kew/-/kew-0.7.0.tgz", "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", "dev": true }, @@ -7726,7 +7797,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -7739,7 +7810,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -8006,6 +8077,11 @@ "loglevel": "^1.4.0" } }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -8031,9 +8107,9 @@ "dev": true }, "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "requires": { "pseudomap": "^1.0.2", @@ -8098,7 +8174,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -8146,7 +8222,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -8157,7 +8233,7 @@ }, "meow": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { @@ -8284,9 +8360,9 @@ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "mississippi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", - "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", "dev": true, "requires": { "concat-stream": "^1.5.0", @@ -8295,10 +8371,22 @@ "flush-write-stream": "^1.0.0", "from2": "^2.1.0", "parallel-transform": "^1.1.0", - "pump": "^2.0.1", + "pump": "^3.0.0", "pumpify": "^1.3.3", "stream-each": "^1.1.0", "through2": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } } }, "mixin-deep": { @@ -8342,7 +8430,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -8383,7 +8471,7 @@ "dependencies": { "commander": { "version": "2.15.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true, "optional": true @@ -8433,9 +8521,9 @@ } }, "moment": { - "version": "2.22.2", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", - "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.23.0.tgz", + "integrity": "sha512-3IE39bHVqFbWWaPOMHZF98Q9c3LDKGTmypMiTM2QygGXXElkFWIH7GxfmlwmY2vwa+wmNsoYZmG2iusf1ZjJoA==" }, "moment-timezone": { "version": "0.5.23", @@ -8503,7 +8591,7 @@ }, "multimatch": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", "dev": true, "requires": { @@ -8515,14 +8603,14 @@ }, "mute-stream": { "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "resolved": "http://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, "nan": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", - "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", "dev": true }, "nanomatch": { @@ -8552,7 +8640,7 @@ }, "ncp": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=", "dev": true }, @@ -8575,9 +8663,9 @@ "dev": true }, "ngeohash": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/ngeohash/-/ngeohash-0.6.0.tgz", - "integrity": "sha1-MpcT6ec9HxpG2SqrC5StuUUz9oc=" + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/ngeohash/-/ngeohash-0.6.3.tgz", + "integrity": "sha512-kltF0cOxgx1AbmVzKxYZaoB0aj7mOxZeHaerEtQV0YaqnkXNq26WWqMmJ6lTqShYxVRWZ/mwvvTrNeOwdslWiw==" }, "nice-try": { "version": "1.0.5", @@ -8586,9 +8674,9 @@ "dev": true }, "nightwatch": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/nightwatch/-/nightwatch-1.0.17.tgz", - "integrity": "sha512-/du74poqA8JNKkgpo00sRxomfKwVtkgkIQ9S66VWK5AWsRRYYzh0wmCMMFjZm2EfagECymP395xwUZ+BW1K9qg==", + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/nightwatch/-/nightwatch-1.0.18.tgz", + "integrity": "sha512-BKosRh/QqpCCMxjnfP+gb8KMQV0y//TNdYDjB0RrU1pXgx2Xjyp46bK8tQWRFfqaxWDj5EKYFIPgvxFBXodIOA==", "dev": true, "requires": { "assertion-error": "^1.1.0", @@ -8651,7 +8739,7 @@ "dependencies": { "semver": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", "dev": true } @@ -8746,12 +8834,23 @@ "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", + "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -8765,18 +8864,18 @@ "integrity": "sha1-9WH0WyszY1K4KXbFHMoRR9U5N/U=" }, "node-releases": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.0.3.tgz", - "integrity": "sha512-ZaZWMsbuDcetpHmYeKWPO6e63pSXLb50M7lJgCbcM2nC/nQC3daNifmtp5a2kp7EWwYfhuvH6zLPWkrF8IiDdw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.3.tgz", + "integrity": "sha512-6VrvH7z6jqqNFY200kdB6HdzkgM96Oaj9v3dqGfgp6mF+cHmU4wyQKZ2/WPDRVoR0Jz9KqbamaBN0ZhdUaysUQ==", "dev": true, "requires": { "semver": "^5.3.0" } }, "node-sass": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.10.0.tgz", - "integrity": "sha512-fDQJfXszw6vek63Fe/ldkYXmRYK/QS6NbvM3i5oEo9ntPDy4XX7BcKZyTKv+/kSSxRtXXc7l+MSwEmYc0CSy6Q==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", + "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", "dev": true, "requires": { "async-foreach": "^0.1.3", @@ -8828,7 +8927,7 @@ }, "underscore": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz", "integrity": "sha1-QLq4S60Z0jAJbo1u9ii/8FXYPbA=" } } @@ -8870,9 +8969,9 @@ "dev": true }, "notepack.io": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-2.1.3.tgz", - "integrity": "sha512-AgSt+cP5XMooho1Ppn8NB3FFaVWefV+qZoZncYTUSch2GAEwlYLcIIbT5YVkMlFeNHnfwOvc4HDlbvrB5BRxXA==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-2.2.0.tgz", + "integrity": "sha512-9b5w3t5VSH6ZPosoYnyDONnUTF8o0UkBw7JLA6eBlYJWyGT1Q3vQa8Hmuj1/X6RYvHjjygBDgw6fJhe0JEojfw==" }, "npm-run-path": { "version": "2.0.2", @@ -9117,13 +9216,13 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-locale": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -9132,7 +9231,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -9327,7 +9426,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -9347,7 +9446,7 @@ }, "parse-asn1": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", "dev": true, "requires": { @@ -9415,7 +9514,7 @@ }, "path-browserify": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, @@ -9433,7 +9532,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -9474,7 +9573,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -9640,14 +9739,14 @@ "integrity": "sha512-1n3Z4p3IOxArEs1VRXnZ/RXdfEniAUS9jb68g58FIXMNkPJeZd+Qh4Uq7/e0LVxAQGos1eIUrqrt4FpjdnEd+Q==" }, "popper.js": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.5.tgz", - "integrity": "sha512-fs4Sd8bZLgEzrk8aS7Em1qh+wcawtE87kRUJQhK6+LndyV1HerX7+LURzAylVaTyWIn5NTB/lyjnWqw/AZ6Yrw==" + "version": "1.14.6", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.6.tgz", + "integrity": "sha512-AGwHGQBKumlk/MDfrSOf0JHhJCImdDMcGNoqKmKkU+68GFazv3CQ6q9r7Ja1sKDZmYWTckY/uLyEznheTDycnA==" }, "portfinder": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.19.tgz", - "integrity": "sha512-23aeQKW9KgHe6citUrG3r9HjeX6vls0h713TAa+CwTKZwNIr/pD2ApaxYF4Um3ZZyq4ar+Siv3+fhoHaIwSOSw==", + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz", + "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==", "dev": true, "requires": { "async": "^1.5.2", @@ -9822,64 +9921,23 @@ } }, "postcss-modules-extract-imports": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", - "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", "dev": true, "requires": { - "postcss": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "postcss": "^7.0.5" } }, "postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.3.tgz", + "integrity": "sha512-jv4CQ8IQ0+TkaAIP7H4kgu/jQbrjte8xU61SYJAIOby+o3H0MGWX6eN1WXUKHccK6/EEjcAERjyIP8MXzAWAbQ==", "dev": true, "requires": { "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" + "postcss": "^7.0.6", + "postcss-value-parser": "^3.3.1" }, "dependencies": { "ansi-styles": { @@ -9903,14 +9961,14 @@ } }, "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz", + "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==", "dev": true, "requires": { "chalk": "^2.4.1", "source-map": "^0.6.1", - "supports-color": "^5.4.0" + "supports-color": "^5.5.0" } }, "supports-color": { @@ -9925,13 +9983,13 @@ } }, "postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.0.1.tgz", + "integrity": "sha512-7+6k9c3/AuZ5c596LJx9n923A/j3nF3ormewYBF1RrIQvjvjXe1xE8V8A1KFyFwXbvnshT6FBZFX0k/F1igneg==", "dev": true, "requires": { "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" + "postcss": "^7.0.6" }, "dependencies": { "ansi-styles": { @@ -9955,14 +10013,14 @@ } }, "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz", + "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==", "dev": true, "requires": { "chalk": "^2.4.1", "source-map": "^0.6.1", - "supports-color": "^5.4.0" + "supports-color": "^5.5.0" } }, "supports-color": { @@ -9977,13 +10035,13 @@ } }, "postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz", + "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==", "dev": true, "requires": { "icss-replace-symbols": "^1.1.0", - "postcss": "^6.0.1" + "postcss": "^7.0.6" }, "dependencies": { "ansi-styles": { @@ -10007,14 +10065,14 @@ } }, "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz", + "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==", "dev": true, "requires": { "chalk": "^2.4.1", "source-map": "^0.6.1", - "supports-color": "^5.4.0" + "supports-color": "^5.5.0" } }, "supports-color": { @@ -10069,7 +10127,7 @@ }, "progress": { "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=" }, "promise-inflight": { @@ -10338,7 +10396,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -10432,7 +10490,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -10497,29 +10555,29 @@ "dev": true }, "regexpu-core": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.2.0.tgz", - "integrity": "sha512-Z835VSnJJ46CNBttalHD/dB+Sj2ezmY6Xp38npwU87peK6mqOzOpV8eYktdkLTEkzzD+JsTcxd84ozd8I14+rw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.4.0.tgz", + "integrity": "sha512-eDDWElbwwI3K0Lo6CqbQbA6FwgtCz4kYTarrri1okfkRLZAqstU+B3voZBCjg8Fl6iq0gXrJG6MvRgLthfvgOA==", "dev": true, "requires": { "regenerate": "^1.4.0", "regenerate-unicode-properties": "^7.0.0", - "regjsgen": "^0.4.0", - "regjsparser": "^0.3.0", + "regjsgen": "^0.5.0", + "regjsparser": "^0.6.0", "unicode-match-property-ecmascript": "^1.0.4", "unicode-match-property-value-ecmascript": "^1.0.2" } }, "regjsgen": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.4.0.tgz", - "integrity": "sha512-X51Lte1gCYUdlwhF28+2YMO0U6WeN0GLpgpA7LK7mbdDnkQYiwvEpmpe0F/cv5L14EbxgrdayAG3JETBv0dbXA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", + "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", "dev": true }, "regjsparser": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.3.0.tgz", - "integrity": "sha512-zza72oZBBHzt64G7DxdqrOo/30bhHkwMUoT0WqfGu98XLd7N+1tsy5MJ96Bk4MD0y74n629RhmrGW6XlnLLwCA==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", + "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -10527,7 +10585,7 @@ "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -10578,7 +10636,7 @@ }, "htmlparser2": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", "dev": true, "requires": { @@ -10590,7 +10648,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -10700,7 +10758,7 @@ }, "require-uncached": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { @@ -10725,7 +10783,7 @@ "dependencies": { "underscore": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "dev": true } @@ -10867,7 +10925,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -11044,7 +11102,7 @@ "dependencies": { "source-map": { "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { @@ -11112,9 +11170,9 @@ } }, "serialize-javascript": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", - "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz", + "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==", "dev": true }, "serve-index": { @@ -11187,7 +11245,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -11231,7 +11289,7 @@ }, "shelljs": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", "dev": true }, @@ -11253,12 +11311,25 @@ } }, "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.0.0.tgz", + "integrity": "sha512-4j2WTWjp3GsZ+AOagyzVbzp4vWGtZ0hEZ/gDY/uTvm6MTxUfTUIsnMIFb1bn8o0RuXiqUw15H1bue8f22Vw2oQ==", "dev": true, "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + } } }, "smart-buffer": { @@ -11456,9 +11527,9 @@ } }, "sortablejs": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.7.0.tgz", - "integrity": "sha1-gKKyNwq9Vo4c7IwnETHvMKkE+ig=" + "version": "1.8.0-rc1", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.8.0-rc1.tgz", + "integrity": "sha512-umyNbQVDwRgc0SZvUB+FRUIUqACnu5vCCmK0zv/xWA3eDSOh+IZsg3GHdWvEOcUBwnykqyk760+YPgVa8HfxFg==" }, "source-list-map": { "version": "2.0.1", @@ -11484,6 +11555,16 @@ "urix": "^0.1.0" } }, + "source-map-support": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", @@ -11523,59 +11604,79 @@ "dev": true }, "spdy": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", - "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz", + "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", "dev": true, "requires": { - "debug": "^2.6.8", - "handle-thing": "^1.2.5", + "debug": "^4.1.0", + "handle-thing": "^2.0.0", "http-deceiver": "^1.2.7", - "safe-buffer": "^5.0.1", "select-hose": "^2.0.0", - "spdy-transport": "^2.0.18" + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } } }, "spdy-transport": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.1.tgz", - "integrity": "sha512-q7D8c148escoB3Z7ySCASadkegMmUZW8Wb/Q1u0/XBgDKMO880rLQDj8Twiew/tYi7ghemKUi/whSYOwE17f5Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "dev": true, "requires": { - "debug": "^2.6.8", - "detect-node": "^2.0.3", + "debug": "^4.1.0", + "detect-node": "^2.0.4", "hpack.js": "^2.1.6", - "obuf": "^1.1.1", - "readable-stream": "^2.2.9", - "safe-buffer": "^5.0.1", - "wbuf": "^1.7.2" + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, "readable-stream": { - "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz", + "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", + "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -11603,13 +11704,13 @@ } }, "split.js": { - "version": "1.5.9", - "resolved": "https://registry.npmjs.org/split.js/-/split.js-1.5.9.tgz", - "integrity": "sha512-uT3yu5neM8wWYKUhftAKTu2k2t5vX9m/1/sXBNjFDyR3u4vfoAN4Edj2vPhnAQ7D14xj8IySwOKyfiV2sf4odA==" + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/split.js/-/split.js-1.5.10.tgz", + "integrity": "sha512-/J52X5c4ZypVwu4WAhD8E1T9uXQtNokvG6mIBHauzyA1aKH6bmETVSv3RPjBXEz6Gcc4mIThgmjGQL39LD16jQ==" }, "split2": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/split2/-/split2-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/split2/-/split2-1.1.1.tgz", "integrity": "sha1-Fi2bGIZfAqsvKtlYVSLbm1TEgfk=", "dev": true, "requires": { @@ -11618,7 +11719,7 @@ }, "sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, @@ -11669,12 +11770,12 @@ } }, "ssri": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", - "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "dev": true, "requires": { - "safe-buffer": "^5.1.1" + "figgy-pudding": "^3.5.1" } }, "stack-trace": { @@ -11750,7 +11851,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -11767,7 +11868,7 @@ }, "stream-browserify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "dev": true, "requires": { @@ -11853,7 +11954,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -11909,7 +12010,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -11926,7 +12027,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -11970,7 +12071,7 @@ }, "supports-color": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, "symbol-tree": { @@ -11980,15 +12081,29 @@ "dev": true }, "table": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/table/-/table-5.1.0.tgz", - "integrity": "sha512-e542in22ZLhD/fOIuXs/8yDZ9W61ltF8daM88rkRNtgTIct+vI2fTnAyu/Db2TCfEcI8i7mjZz6meLq0nW7TYg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/table/-/table-5.1.1.tgz", + "integrity": "sha512-NUjapYb/qd4PeFW03HnAuOJ7OMcBkJlqeClWxeNlQ0lXGSb52oZXGzkO0/I0ARegQ2eUT1g2VDJH0eUxDRcHmw==", "dev": true, "requires": { - "ajv": "^6.5.3", - "lodash": "^4.17.10", - "slice-ansi": "1.0.0", + "ajv": "^6.6.1", + "lodash": "^4.17.11", + "slice-ansi": "2.0.0", "string-width": "^2.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz", + "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } } }, "taffydb": { @@ -12005,7 +12120,7 @@ }, "tar": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, "requires": { @@ -12041,6 +12156,109 @@ } } }, + "terser": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.14.0.tgz", + "integrity": "sha512-KQC1QNKbC/K1ZUjLIWsezW7wkTJuB4v9ptQQUNOzAPVHuVf2LrwEcB0I9t2HTEYUwAFVGiiS6wc+P4ClLDc5FQ==", + "dev": true, + "requires": { + "commander": "~2.17.1", + "source-map": "~0.6.1", + "source-map-support": "~0.5.6" + } + }, + "terser-webpack-plugin": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.1.tgz", + "integrity": "sha512-GGSt+gbT0oKcMDmPx4SRSfJPE1XaN3kQRWG4ghxKQw9cn5G9x6aCKSsgYdvyM0na9NJ4Drv0RG6jbBByZ5CMjw==", + "dev": true, + "requires": { + "cacache": "^11.0.2", + "find-cache-dir": "^2.0.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "terser": "^3.8.1", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "find-cache-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.0.0.tgz", + "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -12060,7 +12278,7 @@ }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -12316,7 +12534,7 @@ }, "tty-browserify": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, @@ -12378,40 +12596,6 @@ "source-map": "~0.6.1" } }, - "uglifyjs-webpack-plugin": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", - "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", - "dev": true, - "requires": { - "cacache": "^10.0.4", - "find-cache-dir": "^1.0.0", - "schema-utils": "^0.4.5", - "serialize-javascript": "^1.4.0", - "source-map": "^0.6.1", - "uglify-es": "^3.3.4", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" - }, - "dependencies": { - "commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", - "dev": true - }, - "uglify-es": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", - "dev": true, - "requires": { - "commander": "~2.13.0", - "source-map": "~0.6.1" - } - } - } - }, "uint64be": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/uint64be/-/uint64be-1.0.1.tgz", @@ -12425,7 +12609,7 @@ }, "underscore": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" }, "underscore-contrib": { @@ -12439,7 +12623,7 @@ "dependencies": { "underscore": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "dev": true } @@ -12780,7 +12964,7 @@ }, "valid-data-url": { "version": "0.1.6", - "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", + "resolved": "http://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", "integrity": "sha512-FXg2qXMzfAhZc0y2HzELNfUeiOjPr+52hU1DNBWiJJ2luXD+dD1R9NA48Ug5aj0ibbxroeGDc/RJv6ThiGgkDw==", "dev": true }, @@ -12796,7 +12980,7 @@ }, "validator": { "version": "9.4.1", - "resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz", + "resolved": "http://registry.npmjs.org/validator/-/validator-9.4.1.tgz", "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA==", "dev": true }, @@ -12824,7 +13008,7 @@ }, "vm-browserify": { "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", "dev": true, "requires": { @@ -12931,9 +13115,9 @@ "dev": true }, "webpack": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.25.1.tgz", - "integrity": "sha512-T0GU/3NRtO4tMfNzsvpdhUr8HnzA4LTdP2zd+e5zd6CdOH5vNKHnAlO+DvzccfhPdzqRrALOFcjYxx7K5DWmvA==", + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.28.3.tgz", + "integrity": "sha512-vLZN9k5I7Nr/XB1IDG9GbZB4yQd1sPuvufMFgJkx0b31fi2LD97KQIjwjxE7xytdruAYfu5S0FLBLjdxmwGJCg==", "dev": true, "requires": { "@webassemblyjs/ast": "1.7.11", @@ -12957,7 +13141,7 @@ "node-libs-browser": "^2.0.0", "schema-utils": "^0.4.4", "tapable": "^1.1.0", - "uglifyjs-webpack-plugin": "^1.2.4", + "terser-webpack-plugin": "^1.1.0", "watchpack": "^1.5.0", "webpack-sources": "^1.3.0" }, @@ -13055,9 +13239,9 @@ } }, "webpack-dev-server": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.10.tgz", - "integrity": "sha512-RqOAVjfqZJtQcB0LmrzJ5y4Jp78lv9CK0MZ1YJDTaTmedMZ9PU9FLMQNrMCfVu8hHzaVLVOJKBlGEHMN10z+ww==", + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz", + "integrity": "sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ==", "dev": true, "requires": { "ansi-html": "0.0.7", @@ -13079,12 +13263,14 @@ "portfinder": "^1.0.9", "schema-utils": "^1.0.0", "selfsigned": "^1.9.1", + "semver": "^5.6.0", "serve-index": "^1.7.2", "sockjs": "0.3.19", "sockjs-client": "1.3.0", - "spdy": "^3.4.1", + "spdy": "^4.0.0", "strip-ansi": "^3.0.0", "supports-color": "^5.1.0", + "url": "^0.11.0", "webpack-dev-middleware": "3.4.0", "webpack-log": "^2.0.0", "yargs": "12.0.2" @@ -13142,6 +13328,21 @@ "xregexp": "4.0.0" } }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -13151,6 +13352,15 @@ "locate-path": "^3.0.0" } }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, "invert-kv": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", @@ -13183,20 +13393,20 @@ "dev": true }, "os-locale": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz", - "integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { - "execa": "^0.10.0", + "execa": "^1.0.0", "lcid": "^2.0.0", "mem": "^4.0.0" } }, "p-limit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", - "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -13217,6 +13427,16 @@ "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", @@ -13292,7 +13512,7 @@ }, "webpack-node-externals": { "version": "1.7.2", - "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz", + "resolved": "http://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz", "integrity": "sha512-ajerHZ+BJKeCLviLUUmnyd5B4RavLF76uv3cs6KNuO8W+HuQaEs0y0L7o40NQxdPy5w0pcv8Ew7yPUAQG0UdCg==", "dev": true }, @@ -13486,7 +13706,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -13505,7 +13725,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -13660,7 +13880,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { diff --git a/package.json b/package.json index eb1cdfcf..81c04265 100644 --- a/package.json +++ b/package.json @@ -30,18 +30,18 @@ "main": "build/node/CyberChef.js", "bugs": "https://github.com/gchq/CyberChef/issues", "devDependencies": { - "@babel/core": "^7.1.5", - "@babel/preset-env": "^7.1.5", - "autoprefixer": "^9.3.1", + "@babel/core": "^7.2.2", + "@babel/preset-env": "^7.2.3", + "autoprefixer": "^9.4.3", "babel-loader": "^8.0.4", - "bootstrap": "^4.1.3", + "bootstrap": "^4.2.1", "chromedriver": "^2.45.0", - "colors": "^1.3.2", - "css-loader": "^1.0.1", - "eslint": "^5.8.0", + "colors": "^1.3.3", + "css-loader": "^2.1.0", + "eslint": "^5.11.1", "exports-loader": "^0.7.0", "extract-text-webpack-plugin": "^4.0.0-alpha0", - "file-loader": "^2.0.0", + "file-loader": "^3.0.1", "grunt": "^1.0.3", "grunt-accessibility": "~6.0.0", "grunt-chmod": "~1.1.1", @@ -58,8 +58,8 @@ "imports-loader": "^0.8.0", "ink-docstrap": "^1.3.2", "jsdoc-babel": "^0.5.0", - "nightwatch": "^1.0.17", - "node-sass": "^4.10.0", + "nightwatch": "^1.0.18", + "node-sass": "^4.11.0", "postcss-css-variables": "^0.11.0", "postcss-import": "^12.0.1", "postcss-loader": "^3.0.0", @@ -69,9 +69,9 @@ "style-loader": "^0.23.1", "url-loader": "^1.1.2", "web-resource-inliner": "^4.2.1", - "webpack": "^4.25.1", + "webpack": "^4.28.3", "webpack-bundle-analyzer": "^3.0.3", - "webpack-dev-server": "^3.1.10", + "webpack-dev-server": "^3.1.14", "webpack-node-externals": "^1.7.2", "worker-loader": "^2.0.0" }, @@ -83,7 +83,7 @@ "bignumber.js": "^8.0.1", "bootstrap-colorpicker": "^2.5.3", "bootstrap-material-design": "^4.1.1", - "bson": "^3.0.2", + "bson": "^4.0.1", "chi-squared": "^1.1.0", "crypto-api": "^0.8.3", "crypto-js": "^3.1.9-1", @@ -94,35 +94,35 @@ "esmangle": "^1.0.1", "esprima": "^4.0.1", "exif-parser": "^0.1.12", - "file-saver": "^2.0.0-rc.4", + "file-saver": "^2.0.0", "highlight.js": "^9.13.1", "jimp": "^0.6.0", "jquery": "^3.3.1", "js-crc": "^0.2.0", "js-sha3": "^0.8.0", - "jsesc": "^2.5.1", + "jsesc": "^2.5.2", "jsonpath": "^1.0.0", - "jsonwebtoken": "^8.3.0", + "jsonwebtoken": "^8.4.0", "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", - "moment": "^2.22.2", + "moment": "^2.23.0", "moment-timezone": "^0.5.23", - "ngeohash": "^0.6.0", + "ngeohash": "^0.6.3", "node-forge": "^0.7.6", "node-md6": "^0.1.0", - "notepack.io": "^2.1.3", + "notepack.io": "^2.2.0", "nwmatcher": "^1.4.4", "otp": "^0.1.3", - "popper.js": "^1.14.4", + "popper.js": "^1.14.6", "qr-image": "^3.2.0", "scryptsy": "^2.0.0", "snackbarjs": "^1.1.0", - "sortablejs": "^1.7.0", - "split.js": "^1.5.9", + "sortablejs": "^1.8.0-rc1", + "split.js": "^1.5.10", "ssdeep.js": "0.0.2", "ua-parser-js": "^0.7.19", "utf8": "^3.0.0", diff --git a/src/core/operations/BSONDeserialise.mjs b/src/core/operations/BSONDeserialise.mjs index 4e66fc81..3a815285 100644 --- a/src/core/operations/BSONDeserialise.mjs +++ b/src/core/operations/BSONDeserialise.mjs @@ -5,7 +5,7 @@ */ import Operation from "../Operation"; -import bsonjs from "bson"; +import bson from "bson"; import OperationError from "../errors/OperationError"; /** @@ -36,8 +36,6 @@ class BSONDeserialise extends Operation { run(input, args) { if (!input.byteLength) return ""; - const bson = new bsonjs(); - try { const data = bson.deserialize(new Buffer(input)); return JSON.stringify(data, null, 2); diff --git a/src/core/operations/BSONSerialise.mjs b/src/core/operations/BSONSerialise.mjs index ed4d067b..6c91a678 100644 --- a/src/core/operations/BSONSerialise.mjs +++ b/src/core/operations/BSONSerialise.mjs @@ -5,7 +5,7 @@ */ import Operation from "../Operation"; -import bsonjs from "bson"; +import bson from "bson"; import OperationError from "../errors/OperationError"; /** @@ -36,8 +36,6 @@ class BSONSerialise extends Operation { run(input, args) { if (!input) return new ArrayBuffer(); - const bson = new bsonjs(); - try { const data = JSON.parse(input); return bson.serialize(data).buffer; diff --git a/src/core/operations/ToMessagePack.mjs b/src/core/operations/ToMessagePack.mjs index ffcf3b43..3de1daa7 100644 --- a/src/core/operations/ToMessagePack.mjs +++ b/src/core/operations/ToMessagePack.mjs @@ -38,7 +38,9 @@ class ToMessagePack extends Operation { if (ENVIRONMENT_IS_WORKER()) { return notepack.encode(input); } else { - return notepack.encode(input).buffer; + const res = notepack.encode(input); + // Safely convert from Node Buffer to ArrayBuffer using the correct view of the data + return (new Uint8Array(res)).buffer; } } catch (err) { throw new OperationError(`Could not encode JSON to MessagePack: ${err}`); diff --git a/src/web/stylesheets/layout/_controls.css b/src/web/stylesheets/layout/_controls.css index b181cb25..380cd284 100755 --- a/src/web/stylesheets/layout/_controls.css +++ b/src/web/stylesheets/layout/_controls.css @@ -15,7 +15,7 @@ width: 100%; height: var(--controls-height); bottom: 0; - padding: 10px; + padding: 0; padding-top: 12px; border-top: 1px solid var(--primary-border-colour); background-color: var(--secondary-background-colour); From 4e466c788687e42449aa1330675b8e60b170a29e Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 1 Jan 2019 19:19:16 +0000 Subject: [PATCH 059/147] 8.19.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index da318772..fb5763e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.0", + "version": "8.19.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 81c04265..809f02f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.0", + "version": "8.19.1", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 017dde364cc118e3d0b17c9a94a028a2d0964bba Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 1 Jan 2019 19:22:10 +0000 Subject: [PATCH 060/147] 8.19.2 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb5763e8..295a681e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.1", + "version": "8.19.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 809f02f0..bb2ae1c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.1", + "version": "8.19.2", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 29411c903f8e03ec82d7a4d3ffad4627cbf71e0d Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 1 Jan 2019 19:56:12 +0000 Subject: [PATCH 061/147] Added increase-memory-limit plugin to TravisCI build process to reduce 'JavaScript heap out of memory' errors. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9e76ec72..6b49c382 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,8 @@ addons: install: npm install before_script: - npm install -g grunt + - npm install -g increase-memory-limit + - increase-memory-limit script: - grunt lint - grunt test From 1bf513ca74d79122988e7352ef6c4889540997b3 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 1 Jan 2019 19:56:20 +0000 Subject: [PATCH 062/147] 8.19.3 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 295a681e..0e4f1aee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.2", + "version": "8.19.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index bb2ae1c9..0df61a55 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.2", + "version": "8.19.3", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From c86007da718381e27409f14adf8f361c9640b615 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 1 Jan 2019 20:15:16 +0000 Subject: [PATCH 063/147] Removed increase-memory-limit plugin in favour of NODE_OPTIONS environment variable. --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6b49c382..dafa8911 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,7 @@ addons: install: npm install before_script: - npm install -g grunt - - npm install -g increase-memory-limit - - increase-memory-limit + - export NODE_OPTIONS=--max_old_space_size=1024 script: - grunt lint - grunt test From bc00fa0694a0931e3a87a16386253e204cbf26b5 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 1 Jan 2019 20:15:29 +0000 Subject: [PATCH 064/147] 8.19.4 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0e4f1aee..d5c0f24d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.3", + "version": "8.19.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0df61a55..336ddddb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.3", + "version": "8.19.4", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From cb2c376c635372f9e0d15b2d661d4db235faa526 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 1 Jan 2019 20:27:38 +0000 Subject: [PATCH 065/147] Increasing Node memory limit from 1G to 2G --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dafa8911..75f2054a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ addons: install: npm install before_script: - npm install -g grunt - - export NODE_OPTIONS=--max_old_space_size=1024 + - export NODE_OPTIONS=--max_old_space_size=2048 script: - grunt lint - grunt test From c82971f8dbbca102321690fe01ac70604de8e3ea Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 1 Jan 2019 20:49:24 +0000 Subject: [PATCH 066/147] 8.19.5 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d5c0f24d..57175720 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.4", + "version": "8.19.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 336ddddb..2274ca30 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.4", + "version": "8.19.5", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 0f0e346a02bac4615ab3c7f168d65b2f3146dd8e Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 8 Jan 2019 11:12:02 +0000 Subject: [PATCH 067/147] Add new Subsection operation --- src/core/operations/Subsection.mjs | 141 +++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/core/operations/Subsection.mjs diff --git a/src/core/operations/Subsection.mjs b/src/core/operations/Subsection.mjs new file mode 100644 index 00000000..b9981100 --- /dev/null +++ b/src/core/operations/Subsection.mjs @@ -0,0 +1,141 @@ +/** + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import XRegExp from "xregexp"; +import Operation from "../Operation"; +import Recipe from "../Recipe"; +import Dish from "../Dish"; + +/** + * Subsection operation + */ +class Subsection extends Operation { + + /** + * Subsection constructor + */ + constructor() { + super(); + + this.name = "Subsection"; + this.flowControl = true; + this.module = "Regex"; + this.description = "Select a part of the input data using regex, and run all subsequent operations on each match separately."; + this.infoURL = ""; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Section (regex)", + "type": "string", + "value": "" + }, + { + "name": "Case sensitive matching", + "type": "boolean", + "value": true + }, + { + "name": "Ignore errors", + "type": "boolean", + "value": false + } + ]; + } + + /** + * @param {Object} state - The current state of the recipe. + * @param {number} state.progress - The current position in the recipe. + * @param {Dish} state.Dish - The Dish being operated on + * @param {Operation[]} state.opList - The list of operations in the recipe + * @returns {Object} - The updated state of the recipe + */ + async run(state) { + const opList = state.opList, + inputType = opList[state.progress].inputType, + outputType = opList[state.progress].outputType, + input = await state.dish.get(inputType), + ings = opList[state.progress].ingValues, + [section, caseSensitive, ignoreErrors] = ings, + subOpList = []; + + if (input && section !== "") { + // Create subOpList for each tranche to operate on + // (all remaining operations unless we encounter a Merge) + for (let i = state.progress + 1; i < opList.length; i++) { + if (opList[i].name === "Merge" && !opList[i].disabled) { + break; + } else { + subOpList.push(opList[i]); + } + } + + let flags = "g", + inOffset = 0, + output = "", + m, + progress = 0; + if (!caseSensitive) + flags += "i"; + const regex = new XRegExp(section, flags), + recipe = new Recipe(); + + recipe.addOperations(subOpList); + state.forkOffset += state.progress + 1; + + // Take a deep(ish) copy of the ingredient values + const ingValues = subOpList.map(op => JSON.parse(JSON.stringify(op.ingValues))); + let matched = false; + + // Run recipe over each match + while ((m = regex.exec(input))) { + matched = true; + // Add up to match + let matchStr = m[0]; + if (m.length === 1) { + output += input.slice(inOffset, m.index); + inOffset = regex.lastIndex; + } else if (m.length >= 2) { + matchStr = m[1]; + // Need to add some of the matched string that isn't in the capture group + output += input.slice(inOffset, m.index + m[0].indexOf(m[1])); + // Set i to be after the end of the first capture group + inOffset = regex.lastIndex - (m[0].length - (m[0].indexOf(m[1]) + m[1].length)); + } + // Baseline ing values for each tranche so that registers are reset + subOpList.forEach((op, i) => { + op.ingValues = JSON.parse(JSON.stringify(ingValues[i])); + }); + + const dish = new Dish(); + dish.set(matchStr, inputType); + + try { + progress = await recipe.execute(dish, 0, state); + } catch (err) { + if (!ignoreErrors) { + throw err; + } + progress = err.progress + 1; + } + output += await dish.get(outputType); + } + // If no matches were found, advance progress to after a Merge op + // Otherwise, the operations below Subsection will be run on all the input data + if (!matched) { + state.progress += subOpList.length + 1; + } + + output += input.slice(inOffset); + state.progress += progress; + state.dish.set(output, outputType); + } + return state; + } + +} + +export default Subsection; From 1a827ef44f034fc90c652e594fc97b4866935488 Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 8 Jan 2019 11:17:06 +0000 Subject: [PATCH 068/147] Add Subsection to Flow Control category --- src/core/config/Categories.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index e9fe3399..832c91e8 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -364,6 +364,7 @@ "ops": [ "Magic", "Fork", + "Subsection", "Merge", "Register", "Label", From 8ac5b484938fe86da5fc332f0e6c4f686ae73db1 Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 8 Jan 2019 11:51:33 +0000 Subject: [PATCH 069/147] Update operation description --- src/core/operations/Subsection.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/Subsection.mjs b/src/core/operations/Subsection.mjs index b9981100..94258b6d 100644 --- a/src/core/operations/Subsection.mjs +++ b/src/core/operations/Subsection.mjs @@ -23,7 +23,7 @@ class Subsection extends Operation { this.name = "Subsection"; this.flowControl = true; this.module = "Regex"; - this.description = "Select a part of the input data using regex, and run all subsequent operations on each match separately."; + this.description = "Select a part of the input data using a regular expression (regex), and run all subsequent operations on each match separately.

You can use up to one capture group, where the recipe will only be run on the data in the capture group. If there's more than one capture group, only the first one will be operated on."; this.infoURL = ""; this.inputType = "string"; this.outputType = "string"; From 5ac469b17467f8f279d592b34e00dd629227f95e Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 8 Jan 2019 16:19:58 +0000 Subject: [PATCH 070/147] Added yara rule support --- package-lock.json | 151 +++++++++++++++--------------- package.json | 1 + src/core/operations/YaraRules.mjs | 68 ++++++++++++++ 3 files changed, 147 insertions(+), 73 deletions(-) create mode 100644 src/core/operations/YaraRules.mjs diff --git a/package-lock.json b/package-lock.json index 4f1be0a3..f1980a00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1171,7 +1171,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1284,7 +1284,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -1369,7 +1369,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1457,7 +1457,7 @@ }, "axios": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "dev": true, "requires": { @@ -1863,7 +1863,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -1900,7 +1900,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -1950,7 +1950,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -2015,7 +2015,7 @@ }, "cacache": { "version": "10.0.4", - "resolved": "http://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", "dev": true, "requires": { @@ -2092,7 +2092,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -2123,7 +2123,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -2590,7 +2590,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -2603,7 +2603,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -2721,7 +2721,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -3055,7 +3055,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -3119,7 +3119,7 @@ "dependencies": { "domelementtype": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", "dev": true }, @@ -3307,7 +3307,7 @@ }, "entities": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", "dev": true }, @@ -3731,7 +3731,7 @@ }, "eventemitter2": { "version": "0.4.14", - "resolved": "http://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", "dev": true }, @@ -3743,7 +3743,7 @@ }, "events": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "dev": true }, @@ -4149,7 +4149,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -4377,7 +4377,7 @@ }, "fs-extra": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", "dev": true, "requires": { @@ -5023,7 +5023,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -5103,7 +5103,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -5173,7 +5173,7 @@ }, "grunt-cli": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", "dev": true, "requires": { @@ -5221,7 +5221,7 @@ "dependencies": { "shelljs": { "version": "0.5.3", - "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", "integrity": "sha1-xUmCuZbHbvDB5rWfvcWCX1txMRM=", "dev": true } @@ -5241,7 +5241,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -5269,7 +5269,7 @@ }, "grunt-contrib-jshint": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", "integrity": "sha1-Np2QmyWTxA6L55lAshNAhQx5Oaw=", "dev": true, "requires": { @@ -5368,7 +5368,7 @@ "dependencies": { "colors": { "version": "1.1.2", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true } @@ -5432,7 +5432,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -5450,7 +5450,7 @@ }, "handle-thing": { "version": "1.2.5", - "resolved": "http://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", "dev": true }, @@ -5725,7 +5725,7 @@ }, "htmlparser2": { "version": "3.8.3", - "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", "dev": true, "requires": { @@ -5744,7 +5744,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -5773,7 +5773,7 @@ }, "http-proxy-middleware": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { @@ -6225,7 +6225,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -6750,7 +6750,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -6856,7 +6856,7 @@ }, "kew": { "version": "0.7.0", - "resolved": "http://registry.npmjs.org/kew/-/kew-0.7.0.tgz", + "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", "dev": true }, @@ -6928,6 +6928,11 @@ "resolved": "https://registry.npmjs.org/lex-parser/-/lex-parser-0.1.4.tgz", "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, + "libyara-wasm": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.2.tgz", + "integrity": "sha512-Q4Biyfwiwrz7u25j7HNbGjJkcuekMJJeGCwbUgc7s7GMriZBSctCJPzlP5MBKgMzGMe0fNc9Hu5EP6Y1lBFnWg==" + }, "livereload-js": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", @@ -6948,7 +6953,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -6961,7 +6966,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -7196,7 +7201,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -7255,7 +7260,7 @@ }, "meow": { "version": "3.7.0", - "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { @@ -7432,7 +7437,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { @@ -7554,7 +7559,7 @@ }, "ncp": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=", "dev": true }, @@ -7617,7 +7622,7 @@ "dependencies": { "semver": { "version": "5.3.0", - "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", "dev": true } @@ -7756,7 +7761,7 @@ "dependencies": { "colors": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" }, "underscore": { @@ -8015,13 +8020,13 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-locale": { "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -8030,7 +8035,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -8173,7 +8178,7 @@ }, "parse-asn1": { "version": "5.1.1", - "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", "dev": true, "requires": { @@ -8231,7 +8236,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -8272,7 +8277,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -8437,7 +8442,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -8839,7 +8844,7 @@ }, "progress": { "version": "1.1.8", - "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=" }, "promise-inflight": { @@ -8864,13 +8869,13 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true }, "winston": { "version": "2.1.1", - "resolved": "http://registry.npmjs.org/winston/-/winston-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", "dev": true, "requires": { @@ -8885,7 +8890,7 @@ "dependencies": { "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true }, @@ -9064,7 +9069,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -9253,7 +9258,7 @@ "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -9304,7 +9309,7 @@ }, "htmlparser2": { "version": "3.3.0", - "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", "dev": true, "requires": { @@ -9316,7 +9321,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -9426,7 +9431,7 @@ }, "require-uncached": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { @@ -9593,7 +9598,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -9914,7 +9919,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -9958,7 +9963,7 @@ }, "shelljs": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", "dev": true }, @@ -10610,7 +10615,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -10627,7 +10632,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -10706,7 +10711,7 @@ }, "tar": { "version": "2.2.1", - "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, "requires": { @@ -10734,7 +10739,7 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -11381,7 +11386,7 @@ "dependencies": { "async": { "version": "0.9.2", - "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -11407,7 +11412,7 @@ }, "valid-data-url": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", "integrity": "sha512-FXg2qXMzfAhZc0y2HzELNfUeiOjPr+52hU1DNBWiJJ2luXD+dD1R9NA48Ug5aj0ibbxroeGDc/RJv6ThiGgkDw==", "dev": true }, @@ -11423,7 +11428,7 @@ }, "validator": { "version": "9.4.1", - "resolved": "http://registry.npmjs.org/validator/-/validator-9.4.1.tgz", + "resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz", "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA==", "dev": true }, @@ -11847,7 +11852,7 @@ }, "webpack-node-externals": { "version": "1.7.2", - "resolved": "http://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz", "integrity": "sha512-ajerHZ+BJKeCLviLUUmnyd5B4RavLF76uv3cs6KNuO8W+HuQaEs0y0L7o40NQxdPy5w0pcv8Ew7yPUAQG0UdCg==", "dev": true }, @@ -11944,14 +11949,14 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true, "optional": true }, "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true, "optional": true @@ -11984,7 +11989,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { diff --git a/package.json b/package.json index 4fac8034..8c0740ca 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "jsonwebtoken": "^8.3.0", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", + "libyara-wasm": "0.0.2", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", diff --git a/src/core/operations/YaraRules.mjs b/src/core/operations/YaraRules.mjs new file mode 100644 index 00000000..9f2d9b79 --- /dev/null +++ b/src/core/operations/YaraRules.mjs @@ -0,0 +1,68 @@ +/** + * @author Matt C [matt@artemisbot.uk] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import Yara from "libyara-wasm"; + +/** + * Yara Rules operation + */ +class YaraRules extends Operation { + + /** + * YaraRules constructor + */ + constructor() { + super(); + + this.name = "Yara Rules"; + this.module = "Yara"; + this.description = "Yara support"; + this.infoURL = "https://en.wikipedia.org/wiki/YARA"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [{ + name: "Rules", + type: "string", + value: "" + }]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + return new Promise((resolve, reject) => { + Yara().then(yara => { + const resp = yara.run(input, args[0]); + if (resp.compileErrors.size() > 0) { + for (let i = 0; i < resp.compileErrors.size(); i++) { + const compileError = resp.compileErrors.get(i); + reject(new OperationError(`Error on line ${compileError.lineNumber}: ${compileError.message}`)); + } + } + const matchedRules = resp.matchedRules; + let matchString = ""; + for (let i = 0; i < matchedRules.keys().size(); i++) { + const ruleMatches = matchedRules.get(matchedRules.keys().get(i)); + matchString += `Rule "${matchedRules.keys().get(i)}" matches:\n`; + + for (let j = 0; j < ruleMatches.size(); j++) { + const match = ruleMatches.get(j); + matchString += `Position ${match.location}, length ${match.matchLength}, data: ${match.data}\n`; + } + } + resolve(matchString); + }); + }); + } + +} + +export default YaraRules; From 766de7e6fa2475ab4d3a1d2d5ce5f1f5f6862ab0 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 8 Jan 2019 17:51:43 +0000 Subject: [PATCH 071/147] Fixed bug in 'Regular expression' operation when highlighting lookaheads --- package-lock.json | 128 +++++++++++----------- src/core/operations/RegularExpression.mjs | 31 ++---- 2 files changed, 76 insertions(+), 83 deletions(-) diff --git a/package-lock.json b/package-lock.json index 57175720..d19abbcc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1497,7 +1497,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1571,7 +1571,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -1615,7 +1615,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -1700,7 +1700,7 @@ }, "util": { "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1848,7 +1848,7 @@ }, "axios": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "dev": true, "requires": { @@ -2286,7 +2286,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -2323,7 +2323,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -2388,7 +2388,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -2551,7 +2551,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -2600,7 +2600,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -2779,7 +2779,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -3133,7 +3133,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -3146,7 +3146,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -3322,7 +3322,7 @@ }, "regexpu-core": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", "dev": true, "requires": { @@ -3339,7 +3339,7 @@ }, "regjsparser": { "version": "0.1.5", - "resolved": "http://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { @@ -4271,7 +4271,7 @@ }, "source-map": { "version": "0.1.43", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", "requires": { "amdefine": ">=0.0.4" @@ -4461,7 +4461,7 @@ "dependencies": { "source-map": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", "integrity": "sha1-D+llA6yGpa213mP05BKuSHLNvoY=", "dev": true } @@ -4794,7 +4794,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -4829,7 +4829,7 @@ }, "findup-sync": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", "dev": true, "requires": { @@ -4896,7 +4896,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -5019,7 +5019,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -5098,12 +5098,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5123,7 +5125,8 @@ "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", @@ -5271,6 +5274,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5660,7 +5664,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -5823,7 +5827,7 @@ }, "globby": { "version": "6.1.0", - "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { @@ -5935,7 +5939,7 @@ }, "resolve": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true } @@ -5981,7 +5985,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -6125,7 +6129,7 @@ "dependencies": { "colors": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true } @@ -6189,7 +6193,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -6409,7 +6413,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -6970,7 +6974,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -7025,7 +7029,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { @@ -7429,7 +7433,7 @@ }, "underscore": { "version": "1.8.3", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", "dev": true } @@ -8222,7 +8226,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -8471,7 +8475,7 @@ "dependencies": { "commander": { "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true, "optional": true @@ -8591,7 +8595,7 @@ }, "multimatch": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", "dev": true, "requires": { @@ -8603,7 +8607,7 @@ }, "mute-stream": { "version": "0.0.7", - "resolved": "http://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, @@ -8922,12 +8926,12 @@ "dependencies": { "colors": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/colors/-/colors-0.5.1.tgz", "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" }, "underscore": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz", "integrity": "sha1-QLq4S60Z0jAJbo1u9ii/8FXYPbA=" } } @@ -9426,7 +9430,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -9514,7 +9518,7 @@ }, "path-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, @@ -10152,13 +10156,13 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true }, "winston": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", + "resolved": "http://registry.npmjs.org/winston/-/winston-2.1.1.tgz", "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", "dev": true, "requires": { @@ -10173,7 +10177,7 @@ "dependencies": { "colors": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true }, @@ -10490,7 +10494,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -10783,7 +10787,7 @@ "dependencies": { "underscore": { "version": "1.6.0", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "dev": true } @@ -11102,7 +11106,7 @@ "dependencies": { "source-map": { "version": "0.4.4", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { @@ -11710,7 +11714,7 @@ }, "split2": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/split2/-/split2-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.1.1.tgz", "integrity": "sha1-Fi2bGIZfAqsvKtlYVSLbm1TEgfk=", "dev": true, "requires": { @@ -11719,7 +11723,7 @@ }, "sprintf-js": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, @@ -11851,7 +11855,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -11868,7 +11872,7 @@ }, "stream-browserify": { "version": "2.0.1", - "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "dev": true, "requires": { @@ -11954,7 +11958,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -12071,7 +12075,7 @@ }, "supports-color": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, "symbol-tree": { @@ -12534,7 +12538,7 @@ }, "tty-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, @@ -12609,7 +12613,7 @@ }, "underscore": { "version": "1.7.0", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" }, "underscore-contrib": { @@ -12623,7 +12627,7 @@ "dependencies": { "underscore": { "version": "1.6.0", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "dev": true } @@ -12938,7 +12942,7 @@ "dependencies": { "async": { "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -13008,7 +13012,7 @@ }, "vm-browserify": { "version": "0.0.4", - "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", "dev": true, "requires": { @@ -13666,14 +13670,14 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true, "optional": true }, "colors": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true, "optional": true @@ -13725,7 +13729,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -13880,7 +13884,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { diff --git a/src/core/operations/RegularExpression.mjs b/src/core/operations/RegularExpression.mjs index 03918982..cce65c63 100644 --- a/src/core/operations/RegularExpression.mjs +++ b/src/core/operations/RegularExpression.mjs @@ -228,40 +228,29 @@ function regexList (input, regex, displayTotal, matches, captureGroups) { function regexHighlight (input, regex, displayTotal) { let output = "", title = "", - m, hl = 1, - i = 0, total = 0; - while ((m = regex.exec(input))) { - // Moves pointer when an empty string is matched (prevents infinite loop) - if (m.index === regex.lastIndex) { - regex.lastIndex++; - } + output = input.replace(regex, (match, ...args) => { + args.pop(); // Throw away full string + const offset = args.pop(), + groups = args; - // Add up to match - output += Utils.escapeHtml(input.slice(i, m.index)); - - title = `Offset: ${m.index}\n`; - if (m.length > 1) { + title = `Offset: ${offset}\n`; + if (groups.length) { title += "Groups:\n"; - for (let n = 1; n < m.length; ++n) { - title += `\t${n}: ${m[n]}\n`; + for (let i = 0; i < groups.length; i++) { + title += `\t${i+1}: ${Utils.escapeHtml(groups[i])}\n`; } } - // Add match with highlighting - output += "" + Utils.escapeHtml(m[0]) + ""; - // Switch highlight hl = hl === 1 ? 2 : 1; - i = regex.lastIndex; total++; - } - // Add all after final match - output += Utils.escapeHtml(input.slice(i, input.length)); + return `${Utils.escapeHtml(match)}`; + }); if (displayTotal) output = "Total found: " + total + "\n\n" + output; From 3a6b2875d5883824f1dfd2eca3982b2f1bbd0c61 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 8 Jan 2019 17:51:47 +0000 Subject: [PATCH 072/147] 8.19.6 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d19abbcc..b8c5b5fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.5", + "version": "8.19.6", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 2274ca30..8dbdc291 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.5", + "version": "8.19.6", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From cb9ab7a2c9696545fbd3bb61dcf37dd18b4f2eff Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 8 Jan 2019 18:29:07 +0000 Subject: [PATCH 073/147] Fixed 'Maximise output' button functionality --- src/web/App.mjs | 13 ++++++++++--- src/web/OutputWaiter.mjs | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/web/App.mjs b/src/web/App.mjs index 1dab16e6..846803a1 100755 --- a/src/web/App.mjs +++ b/src/web/App.mjs @@ -238,12 +238,18 @@ class App { /** * Sets up the adjustable splitter to allow the user to resize areas of the page. + * + * @param {boolean} [minimise=false] - Set this flag if attempting to minimuse frames to 0 width */ - initialiseSplitter() { + initialiseSplitter(minimise=false) { + if (this.columnSplitter) this.columnSplitter.destroy(); + if (this.ioSplitter) this.ioSplitter.destroy(); + this.columnSplitter = Split(["#operations", "#recipe", "#IO"], { sizes: [20, 30, 50], - minSize: [240, 370, 450], + minSize: minimise ? [0, 0, 0] : [240, 370, 450], gutterSize: 4, + expandToMin: false, onDrag: function() { this.manager.recipe.adjustWidth(); }.bind(this) @@ -251,7 +257,8 @@ class App { this.ioSplitter = Split(["#input", "#output"], { direction: "vertical", - gutterSize: 4 + gutterSize: 4, + minSize: minimise ? [0, 0] : [100, 100] }); this.resetLayout(); diff --git a/src/web/OutputWaiter.mjs b/src/web/OutputWaiter.mjs index 7203a16f..28deaffa 100755 --- a/src/web/OutputWaiter.mjs +++ b/src/web/OutputWaiter.mjs @@ -319,6 +319,7 @@ class OutputWaiter { const el = e.target.id === "maximise-output" ? e.target : e.target.parentNode; if (el.getAttribute("data-original-title").indexOf("Maximise") === 0) { + this.app.initialiseSplitter(true); this.app.columnSplitter.collapse(0); this.app.columnSplitter.collapse(1); this.app.ioSplitter.collapse(0); @@ -328,6 +329,7 @@ class OutputWaiter { } else { $(el).attr("data-original-title", "Maximise output pane"); el.querySelector("i").innerHTML = "fullscreen"; + this.app.initialiseSplitter(false); this.app.resetLayout(); } } From fe1332f18e2ea23a92508ed2fc6d4b1fa42c7b32 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 8 Jan 2019 18:29:14 +0000 Subject: [PATCH 074/147] 8.19.7 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b8c5b5fe..81136cc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.6", + "version": "8.19.7", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 8dbdc291..e1b801d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.6", + "version": "8.19.7", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From df8abb099c77babbe6a2a30f4cafc5c3ec6d7207 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 8 Jan 2019 22:23:14 +0000 Subject: [PATCH 075/147] Added code argtype --- package-lock.json | 74 +++++++++++++++---------------- src/core/operations/YaraRules.mjs | 2 +- src/web/HTMLIngredient.mjs | 10 +++++ 3 files changed, 48 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index eae5e06f..7241c6de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1571,7 +1571,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -2779,7 +2779,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -3322,7 +3322,7 @@ }, "regexpu-core": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", "dev": true, "requires": { @@ -3339,7 +3339,7 @@ }, "regjsparser": { "version": "0.1.5", - "resolved": "http://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { @@ -4271,7 +4271,7 @@ }, "source-map": { "version": "0.1.43", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", "requires": { "amdefine": ">=0.0.4" @@ -4461,7 +4461,7 @@ "dependencies": { "source-map": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", "integrity": "sha1-D+llA6yGpa213mP05BKuSHLNvoY=", "dev": true } @@ -4829,7 +4829,7 @@ }, "findup-sync": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", "dev": true, "requires": { @@ -4896,7 +4896,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -5019,7 +5019,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -5660,7 +5660,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -5823,7 +5823,7 @@ }, "globby": { "version": "6.1.0", - "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { @@ -5935,7 +5935,7 @@ }, "resolve": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true } @@ -6409,7 +6409,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -6970,7 +6970,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -7025,7 +7025,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { @@ -7429,7 +7429,7 @@ }, "underscore": { "version": "1.8.3", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", "dev": true } @@ -8227,7 +8227,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -8596,7 +8596,7 @@ }, "multimatch": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", "dev": true, "requires": { @@ -8608,7 +8608,7 @@ }, "mute-stream": { "version": "0.0.7", - "resolved": "http://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, @@ -8932,7 +8932,7 @@ }, "underscore": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz", "integrity": "sha1-QLq4S60Z0jAJbo1u9ii/8FXYPbA=" } } @@ -9431,7 +9431,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -9519,7 +9519,7 @@ }, "path-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, @@ -10495,7 +10495,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -10788,7 +10788,7 @@ "dependencies": { "underscore": { "version": "1.6.0", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "dev": true } @@ -11107,7 +11107,7 @@ "dependencies": { "source-map": { "version": "0.4.4", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { @@ -11715,7 +11715,7 @@ }, "split2": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/split2/-/split2-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.1.1.tgz", "integrity": "sha1-Fi2bGIZfAqsvKtlYVSLbm1TEgfk=", "dev": true, "requires": { @@ -11724,7 +11724,7 @@ }, "sprintf-js": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, @@ -11856,7 +11856,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -11873,7 +11873,7 @@ }, "stream-browserify": { "version": "2.0.1", - "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "dev": true, "requires": { @@ -11959,7 +11959,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -12076,7 +12076,7 @@ }, "supports-color": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, "symbol-tree": { @@ -12539,7 +12539,7 @@ }, "tty-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, @@ -12614,7 +12614,7 @@ }, "underscore": { "version": "1.7.0", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" }, "underscore-contrib": { @@ -12628,7 +12628,7 @@ "dependencies": { "underscore": { "version": "1.6.0", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "dev": true } @@ -13013,7 +13013,7 @@ }, "vm-browserify": { "version": "0.0.4", - "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", "dev": true, "requires": { @@ -13730,7 +13730,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -13885,7 +13885,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { diff --git a/src/core/operations/YaraRules.mjs b/src/core/operations/YaraRules.mjs index 9f2d9b79..3037bdb6 100644 --- a/src/core/operations/YaraRules.mjs +++ b/src/core/operations/YaraRules.mjs @@ -27,7 +27,7 @@ class YaraRules extends Operation { this.outputType = "string"; this.args = [{ name: "Rules", - type: "string", + type: "code", value: "" }]; } diff --git a/src/web/HTMLIngredient.mjs b/src/web/HTMLIngredient.mjs index bb01d7de..0cbb2dad 100755 --- a/src/web/HTMLIngredient.mjs +++ b/src/web/HTMLIngredient.mjs @@ -42,6 +42,16 @@ class HTMLIngredient { i, m; switch (this.type) { + case "code": + html+= `
+ + + ${this.hint ? "" + this.hint + "" : ""}`; + break; case "string": case "binaryString": case "byteArray": From 4c1521a98ee41cd447f691673acbdf08baafa2c3 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 8 Jan 2019 23:26:14 +0000 Subject: [PATCH 076/147] No data matches & warnings support --- src/core/operations/YaraRules.mjs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/core/operations/YaraRules.mjs b/src/core/operations/YaraRules.mjs index 3037bdb6..74de6f29 100644 --- a/src/core/operations/YaraRules.mjs +++ b/src/core/operations/YaraRules.mjs @@ -40,23 +40,32 @@ class YaraRules extends Operation { run(input, args) { return new Promise((resolve, reject) => { Yara().then(yara => { + let matchString = ""; const resp = yara.run(input, args[0]); if (resp.compileErrors.size() > 0) { for (let i = 0; i < resp.compileErrors.size(); i++) { const compileError = resp.compileErrors.get(i); - reject(new OperationError(`Error on line ${compileError.lineNumber}: ${compileError.message}`)); + if (!compileError.warning) { + reject(new OperationError(`Error on line ${compileError.lineNumber}: ${compileError.message}`)); + } else { + matchString += `Warning on line ${compileError.lineNumber}: ${compileError.message}`; + } } } const matchedRules = resp.matchedRules; - let matchString = ""; for (let i = 0; i < matchedRules.keys().size(); i++) { const ruleMatches = matchedRules.get(matchedRules.keys().get(i)); - matchString += `Rule "${matchedRules.keys().get(i)}" matches:\n`; + if (ruleMatches.size() === 0) { + matchString += `Input matches rule "${matchedRules.keys().get(i)}".\n`; + } else { + matchString += `Rule "${matchedRules.keys().get(i)}" matches:\n`; - for (let j = 0; j < ruleMatches.size(); j++) { - const match = ruleMatches.get(j); - matchString += `Position ${match.location}, length ${match.matchLength}, data: ${match.data}\n`; + for (let j = 0; j < ruleMatches.size(); j++) { + const match = ruleMatches.get(j); + matchString += `Position ${match.location}, length ${match.matchLength}, data: ${match.data}\n`; + } } + } resolve(matchString); }); From 26a2fb66625cb9bd703ef49446f7aeca6436028e Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 9 Jan 2019 09:56:55 +0000 Subject: [PATCH 077/147] Increased size of rule inp & expanded memory for wasm --- package-lock.json | 6 +++--- package.json | 2 +- src/web/HTMLIngredient.mjs | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7241c6de..8827fd47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7756,9 +7756,9 @@ "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, "libyara-wasm": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.2.tgz", - "integrity": "sha512-Q4Biyfwiwrz7u25j7HNbGjJkcuekMJJeGCwbUgc7s7GMriZBSctCJPzlP5MBKgMzGMe0fNc9Hu5EP6Y1lBFnWg==" + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.4.tgz", + "integrity": "sha512-Puw8AfHRgAiS2SBvJBlh3DEYU3icU16MciwQK5Fsxel021UK7DcY1A5DAKYanPNeXVztlz/9USZbEneAkcWzvA==" }, "livereload-js": { "version": "2.4.0", diff --git a/package.json b/package.json index 1f207e4b..ffdc5dfd 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", - "libyara-wasm": "0.0.2", + "libyara-wasm": "0.0.4", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", diff --git a/src/web/HTMLIngredient.mjs b/src/web/HTMLIngredient.mjs index 0cbb2dad..4de7e43f 100755 --- a/src/web/HTMLIngredient.mjs +++ b/src/web/HTMLIngredient.mjs @@ -49,6 +49,7 @@ class HTMLIngredient { id="${this.id}" arg-name="${this.name}" value="${this.value}" + rows=5 ${this.disabled ? "disabled" : ""}> ${this.hint ? "" + this.hint + "" : ""}`; break; From 4db2335107ddf519784b4033336087ebf16ec2b0 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 9 Jan 2019 11:45:11 +0000 Subject: [PATCH 078/147] Speedrunning strats (increased speed on big files) --- package-lock.json | 6 +++--- package.json | 2 +- src/core/operations/YaraRules.mjs | 9 +++++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8827fd47..573f8e67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7756,9 +7756,9 @@ "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, "libyara-wasm": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.4.tgz", - "integrity": "sha512-Puw8AfHRgAiS2SBvJBlh3DEYU3icU16MciwQK5Fsxel021UK7DcY1A5DAKYanPNeXVztlz/9USZbEneAkcWzvA==" + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.6.tgz", + "integrity": "sha512-Crnaz5G/ejjZrEYTlyUZIaquR66djW8w8UR8GtgFrpWzhiySPJTcdxwOhGmCku2VhhETPznz20KxBNifBSF+oA==" }, "livereload-js": { "version": "2.4.0", diff --git a/package.json b/package.json index ffdc5dfd..2f9c7d04 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", - "libyara-wasm": "0.0.4", + "libyara-wasm": "0.0.6", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", diff --git a/src/core/operations/YaraRules.mjs b/src/core/operations/YaraRules.mjs index 74de6f29..b6d78a8c 100644 --- a/src/core/operations/YaraRules.mjs +++ b/src/core/operations/YaraRules.mjs @@ -23,7 +23,7 @@ class YaraRules extends Operation { this.module = "Yara"; this.description = "Yara support"; this.infoURL = "https://en.wikipedia.org/wiki/YARA"; - this.inputType = "string"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [{ name: "Rules", @@ -41,7 +41,12 @@ class YaraRules extends Operation { return new Promise((resolve, reject) => { Yara().then(yara => { let matchString = ""; - const resp = yara.run(input, args[0]); + const inpArr = new Uint8Array(input); // I know this is garbage but it's like 1.5 times faster + const inpVec = new yara.vectorChar(); + for (let i = 0; i < inpArr.length; i++) { + inpVec.push_back(inpArr[i]); + } + const resp = yara.run(inpVec, args[0]); if (resp.compileErrors.size() > 0) { for (let i = 0; i < resp.compileErrors.size(); i++) { const compileError = resp.compileErrors.get(i); From ebb632e8882f8f2bad67429c4bf49ab13a211587 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 9 Jan 2019 14:29:14 +0000 Subject: [PATCH 079/147] Added metadata, string identifiers and operation args --- package-lock.json | 6 ++-- package.json | 2 +- src/core/operations/YaraRules.mjs | 58 +++++++++++++++++++++++-------- src/web/HTMLIngredient.mjs | 3 +- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 573f8e67..2b4d058b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7756,9 +7756,9 @@ "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, "libyara-wasm": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.6.tgz", - "integrity": "sha512-Crnaz5G/ejjZrEYTlyUZIaquR66djW8w8UR8GtgFrpWzhiySPJTcdxwOhGmCku2VhhETPznz20KxBNifBSF+oA==" + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.8.tgz", + "integrity": "sha512-ZB+Ya3bEBoanvde47X8RzqpMBHgrPxrTZIJ/UEoatVnOEy2he1IORuotdSkP2o73URRzHGN1jNWDIhTdfbZ3rQ==" }, "livereload-js": { "version": "2.4.0", diff --git a/package.json b/package.json index 2f9c7d04..8e21a0a6 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", - "libyara-wasm": "0.0.6", + "libyara-wasm": "0.0.8", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", diff --git a/src/core/operations/YaraRules.mjs b/src/core/operations/YaraRules.mjs index b6d78a8c..bfdebb13 100644 --- a/src/core/operations/YaraRules.mjs +++ b/src/core/operations/YaraRules.mjs @@ -25,11 +25,28 @@ class YaraRules extends Operation { this.infoURL = "https://en.wikipedia.org/wiki/YARA"; this.inputType = "ArrayBuffer"; this.outputType = "string"; - this.args = [{ - name: "Rules", - type: "code", - value: "" - }]; + this.args = [ + { + name: "Rules", + type: "code", + value: "" + }, + { + name: "Show strings", + type: "boolean", + value: false + }, + { + name: "Show string lengths", + type: "boolean", + value: false + }, + { + name: "Show metadata", + type: "boolean", + value: false + } + ]; } /** @@ -38,6 +55,7 @@ class YaraRules extends Operation { * @returns {string} */ run(input, args) { + const [rules, showStrings, showLengths, showMeta] = args; return new Promise((resolve, reject) => { Yara().then(yara => { let matchString = ""; @@ -46,7 +64,7 @@ class YaraRules extends Operation { for (let i = 0; i < inpArr.length; i++) { inpVec.push_back(inpArr[i]); } - const resp = yara.run(inpVec, args[0]); + const resp = yara.run(inpVec, rules); if (resp.compileErrors.size() > 0) { for (let i = 0; i < resp.compileErrors.size(); i++) { const compileError = resp.compileErrors.get(i); @@ -58,16 +76,26 @@ class YaraRules extends Operation { } } const matchedRules = resp.matchedRules; - for (let i = 0; i < matchedRules.keys().size(); i++) { - const ruleMatches = matchedRules.get(matchedRules.keys().get(i)); - if (ruleMatches.size() === 0) { - matchString += `Input matches rule "${matchedRules.keys().get(i)}".\n`; + for (let i = 0; i < matchedRules.size(); i++) { + const rule = matchedRules.get(i); + const matches = rule.resolvedMatches; + let meta = ""; + if (showMeta && rule.metadata.size() > 0) { + meta += " ["; + for (let j = 0; j < rule.metadata.size(); j++) { + meta += `${rule.metadata.get(j).identifier}: ${rule.metadata.get(j).data}, `; + } + meta = meta.slice(0, -2) + "]"; + } + if (matches.size() === 0 || !(showStrings || showLengths)) { + matchString += `Input matches rule "${rule.ruleName}"${meta}.\n`; } else { - matchString += `Rule "${matchedRules.keys().get(i)}" matches:\n`; - - for (let j = 0; j < ruleMatches.size(); j++) { - const match = ruleMatches.get(j); - matchString += `Position ${match.location}, length ${match.matchLength}, data: ${match.data}\n`; + matchString += `Rule "${rule.ruleName}"${meta} matches:\n`; + for (let j = 0; j < matches.size(); j++) { + const match = matches.get(j); + if (showStrings || showLengths) { + matchString += `Pos ${match.location}, ${showLengths ? `length ${match.matchLength}, ` : ""}identifier ${match.stringIdentifier}${showStrings ? `, data: "${match.data}"` : ""}\n`; + } } } diff --git a/src/web/HTMLIngredient.mjs b/src/web/HTMLIngredient.mjs index 4de7e43f..234c5343 100755 --- a/src/web/HTMLIngredient.mjs +++ b/src/web/HTMLIngredient.mjs @@ -51,7 +51,8 @@ class HTMLIngredient { value="${this.value}" rows=5 ${this.disabled ? "disabled" : ""}> - ${this.hint ? "" + this.hint + "" : ""}`; + ${this.hint ? "" + this.hint + "" : ""} +
`; break; case "string": case "binaryString": From dd9ba4d250b381e45966b74e9109e95b2306939b Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 9 Jan 2019 15:28:50 +0000 Subject: [PATCH 080/147] Fixed problems flagged by n's review --- src/core/Ingredient.mjs | 2 ++ src/core/Operation.mjs | 1 + .../{YaraRules.mjs => YARARules.mjs} | 24 +++++++++++-------- src/web/HTMLIngredient.mjs | 14 ++--------- 4 files changed, 19 insertions(+), 22 deletions(-) rename src/core/operations/{YaraRules.mjs => YARARules.mjs} (79%) diff --git a/src/core/Ingredient.mjs b/src/core/Ingredient.mjs index 00dd5f6d..96cdd400 100755 --- a/src/core/Ingredient.mjs +++ b/src/core/Ingredient.mjs @@ -23,6 +23,7 @@ class Ingredient { this._value = null; this.disabled = false; this.hint = ""; + this.rows = 0; this.toggleValues = []; this.target = null; this.defaultIndex = 0; @@ -45,6 +46,7 @@ class Ingredient { this.defaultValue = ingredientConfig.value; this.disabled = !!ingredientConfig.disabled; this.hint = ingredientConfig.hint || false; + this.rows = ingredientConfig.rows || false; this.toggleValues = ingredientConfig.toggleValues; this.target = typeof ingredientConfig.target !== "undefined" ? ingredientConfig.target : null; this.defaultIndex = typeof ingredientConfig.defaultIndex !== "undefined" ? ingredientConfig.defaultIndex : 0; diff --git a/src/core/Operation.mjs b/src/core/Operation.mjs index 3f6b3e86..d57f885d 100755 --- a/src/core/Operation.mjs +++ b/src/core/Operation.mjs @@ -179,6 +179,7 @@ class Operation { if (ing.toggleValues) conf.toggleValues = ing.toggleValues; if (ing.hint) conf.hint = ing.hint; + if (ing.rows) conf.rows = ing.rows; if (ing.disabled) conf.disabled = ing.disabled; if (ing.target) conf.target = ing.target; if (ing.defaultIndex) conf.defaultIndex = ing.defaultIndex; diff --git a/src/core/operations/YaraRules.mjs b/src/core/operations/YARARules.mjs similarity index 79% rename from src/core/operations/YaraRules.mjs rename to src/core/operations/YARARules.mjs index bfdebb13..094d9f43 100644 --- a/src/core/operations/YaraRules.mjs +++ b/src/core/operations/YARARules.mjs @@ -9,41 +9,45 @@ import OperationError from "../errors/OperationError"; import Yara from "libyara-wasm"; /** - * Yara Rules operation + * YARA Rules operation */ -class YaraRules extends Operation { +class YARARules extends Operation { /** - * YaraRules constructor + * YARARules constructor */ constructor() { super(); - this.name = "Yara Rules"; + this.name = "YARA Rules"; this.module = "Yara"; - this.description = "Yara support"; - this.infoURL = "https://en.wikipedia.org/wiki/YARA"; + this.description = "YARA is a tool developed at VirusTotal, primarily aimed at helping malware researchers to identify and classify malware samples. It matches based on rules specified by the user containing textual or binary patterns and a boolean expression. For help on writing rules, see the YARA documentation."; + this.infoURL = "https://wikipedia.org/wiki/YARA"; this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { name: "Rules", - type: "code", - value: "" + type: "text", + value: "", + rows: 5 }, { name: "Show strings", type: "boolean", + hint: "Show each match's data", value: false }, { name: "Show string lengths", type: "boolean", + hint: "Show the length of each match's data", value: false }, { name: "Show metadata", type: "boolean", + hint: "Show the metadata of each rule", value: false } ]; @@ -59,7 +63,7 @@ class YaraRules extends Operation { return new Promise((resolve, reject) => { Yara().then(yara => { let matchString = ""; - const inpArr = new Uint8Array(input); // I know this is garbage but it's like 1.5 times faster + const inpArr = new Uint8Array(input); const inpVec = new yara.vectorChar(); for (let i = 0; i < inpArr.length; i++) { inpVec.push_back(inpArr[i]); @@ -107,4 +111,4 @@ class YaraRules extends Operation { } -export default YaraRules; +export default YARARules; diff --git a/src/web/HTMLIngredient.mjs b/src/web/HTMLIngredient.mjs index 234c5343..59b7bec7 100755 --- a/src/web/HTMLIngredient.mjs +++ b/src/web/HTMLIngredient.mjs @@ -25,6 +25,7 @@ class HTMLIngredient { this.value = config.value; this.disabled = config.disabled || false; this.hint = config.hint || false; + this.rows = config.rows || false; this.target = config.target; this.defaultIndex = config.defaultIndex || 0; this.toggleValues = config.toggleValues; @@ -42,18 +43,6 @@ class HTMLIngredient { i, m; switch (this.type) { - case "code": - html+= `
- - - ${this.hint ? "" + this.hint + "" : ""} -
`; - break; case "string": case "binaryString": case "byteArray": @@ -241,6 +230,7 @@ class HTMLIngredient { class="form-control arg" id="${this.id}" arg-name="${this.name}" + rows="${this.rows ? this.rows : 3}" ${this.disabled ? "disabled" : ""}>${this.value} ${this.hint ? "" + this.hint + "" : ""}
`; From c49a770c59adb28cfd082dc2c935f7c900902625 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 9 Jan 2019 16:36:34 +0000 Subject: [PATCH 081/147] Tidied up Lorem Ipsum op --- CHANGELOG.md | 5 ++ src/core/config/Categories.json | 4 +- src/core/lib/LoremIpsum.mjs | 81 ++++++++++--------- ...umGenerator.mjs => GenerateLoremIpsum.mjs} | 12 +-- 4 files changed, 58 insertions(+), 44 deletions(-) rename src/core/operations/{LoremIpsumGenerator.mjs => GenerateLoremIpsum.mjs} (85%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a71bb41..d9b8eff0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ 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.20.0] - 2019-01-09 +- 'Generate Lorem Ipsum' operation added [@klaxon1] | [#455] + ### [8.19.0] - 2018-12-30 - UI test suite added to confirm that the app loads correctly in a reasonable time and that various operations from each module can be run [@n1474335] | [#458] @@ -88,6 +91,7 @@ All major and minor version changes will be documented in this file. Details of +[8.20.0]: https://github.com/gchq/CyberChef/releases/tag/v8.20.0 [8.19.0]: https://github.com/gchq/CyberChef/releases/tag/v8.19.0 [8.18.0]: https://github.com/gchq/CyberChef/releases/tag/v8.18.0 [8.17.0]: https://github.com/gchq/CyberChef/releases/tag/v8.17.0 @@ -159,4 +163,5 @@ All major and minor version changes will be documented in this file. Details of [#446]: https://github.com/gchq/CyberChef/pull/446 [#448]: https://github.com/gchq/CyberChef/pull/448 [#449]: https://github.com/gchq/CyberChef/pull/449 +[#455]: https://github.com/gchq/CyberChef/pull/455 [#458]: https://github.com/gchq/CyberChef/pull/458 diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 3678a88b..3e792b99 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -374,9 +374,9 @@ "Generate QR Code", "Parse QR Code", "Haversine distance", + "Generate Lorem Ipsum", "Numberwang", - "XKCD Random Number", - "Lorem Ipsum Generator" + "XKCD Random Number" ] }, { diff --git a/src/core/lib/LoremIpsum.mjs b/src/core/lib/LoremIpsum.mjs index 9712a429..d7fff69b 100644 --- a/src/core/lib/LoremIpsum.mjs +++ b/src/core/lib/LoremIpsum.mjs @@ -2,16 +2,16 @@ * Lorem Ipsum generator. * * @author Klaxon [klaxon@veyr.com] - * @copyright Crown Copyright 2016 + * @copyright Crown Copyright 2018 * @license Apache-2.0 */ - /** - * generate lorem ipsum paragraphs. - * - * @param {number} length - * @returns {string} - */ +/** + * Generate lorem ipsum paragraphs. + * + * @param {number} length + * @returns {string} + */ export function GenerateParagraphs(length=3) { const paragraphs = []; while (paragraphs.length < length) { @@ -29,12 +29,13 @@ export function GenerateParagraphs(length=3) { return paragraphs.join(""); } + /** -* generate lorem ipsum sentences. -* -* @param {number} length -* @returns {string} -*/ + * Generate lorem ipsum sentences. + * + * @param {number} length + * @returns {string} + */ export function GenerateSentences(length=3) { const sentences = []; while (sentences.length < length) { @@ -46,12 +47,13 @@ export function GenerateSentences(length=3) { return paragraphs.join(""); } + /** -* generate lorem ipsum words. -* -* @param {number} length -* @returns {string} -*/ + * Generate lorem ipsum words. + * + * @param {number} length + * @returns {string} + */ export function GenerateWords(length=3) { const words = getWords(length); const sentences = wordsToSentences(words); @@ -59,19 +61,21 @@ export function GenerateWords(length=3) { return paragraphs.join(""); } - /** - * generate lorem ipsum bytes. - * - * @param {number} length - * @returns {string} - */ + +/** + * Generate lorem ipsum bytes. + * + * @param {number} length + * @returns {string} + */ export function GenerateBytes(length=3) { const str = GenerateWords(length/3); return str.slice(0, length); } + /** - * get array of randomly selected words from the lorem ipsum wordList. + * Get array of randomly selected words from the lorem ipsum wordList. * * @param {number} length * @returns {string[]} @@ -84,16 +88,16 @@ function getWords(length=3) { while (words.length < length){ do { word = wordList[Math.floor(Math.random() * wordList.length)]; - } - while (previousWord === word); + } while (previousWord === word); words.push(word); previousWord = word; } return words; } + /** - * convert an array or words into an array of sentences" + * Convert an array of words into an array of sentences * * @param {string[]} words * @returns {string[]} @@ -112,8 +116,9 @@ function wordsToSentences(words) { return sentences; } + /** - * convert an array or sentences into an array of paragraphs" + * Convert an array of sentences into an array of paragraphs * * @param {string[]} sentences * @returns {string[]} @@ -130,15 +135,16 @@ function sentencesToParagraphs(sentences) { return paragraphs; } + /** - * format an array of words into a sentence. + * Format an array of words into a sentence. * * @param {string[]} words * @returns {string} * @private */ function formatSentence(words) { - //0.35 chance of a comma being added randomly to the sentence. + // 0.35 chance of a comma being added randomly to the sentence. if (Math.random() < PROBABILITY_OF_A_COMMA) { const pos = Math.round(Math.random()*(words.length-1)); words[pos] +=","; @@ -149,8 +155,9 @@ function formatSentence(words) { return sentence; } + /** - * format an array of sentences into a paragraph + * Format an array of sentences into a paragraph. * * @param {string[]} sentences * @returns {string} @@ -162,10 +169,11 @@ function formatParagraph(sentences) { return paragraph; } + /** - * get a random number based on a mean and standard deviation. + * Get a random number based on a mean and standard deviation. * - * @param {number} Mean + * @param {number} mean * @param {number} stdDev * @returns {number} * @private @@ -174,13 +182,13 @@ function getRandomLength(mean, stdDev) { let length; do { length = Math.round((Math.random()*2-1)+(Math.random()*2-1)+(Math.random()*2-1)*stdDev+mean); - } - while (length <= 0); + } while (length <= 0); return length; } + /** - * replace first 5 words with "Lorem ipsum dolor sit amet" + * Replace first 5 words with "Lorem ipsum dolor sit amet" * * @param {string[]} str * @returns {string[]} @@ -200,6 +208,7 @@ function replaceStart(str) { } } + const SENTENCE_LENGTH_MEAN = 15; const SENTENCE_LENGTH_STD_DEV = 9; const PARAGRAPH_LENGTH_MEAN = 5; diff --git a/src/core/operations/LoremIpsumGenerator.mjs b/src/core/operations/GenerateLoremIpsum.mjs similarity index 85% rename from src/core/operations/LoremIpsumGenerator.mjs rename to src/core/operations/GenerateLoremIpsum.mjs index 228daaa1..fb5ecd17 100644 --- a/src/core/operations/LoremIpsumGenerator.mjs +++ b/src/core/operations/GenerateLoremIpsum.mjs @@ -9,17 +9,17 @@ import OperationError from "../errors/OperationError"; import { GenerateParagraphs, GenerateSentences, GenerateWords, GenerateBytes } from "../lib/LoremIpsum"; /** - * Lorem Ipsum Generator operation + * Generate Lorem Ipsum operation */ -class LoremIpsumGenerator extends Operation { +class GenerateLoremIpsum extends Operation { /** - * LoremIpsumGenerator constructor + * GenerateLoremIpsum constructor */ constructor() { super(); - this.name = "Lorem Ipsum Generator"; + this.name = "Generate Lorem Ipsum"; this.module = "Default"; this.description = "Generate varying length lorem ipsum placeholder text."; this.infoURL = "https://wikipedia.org/wiki/Lorem_ipsum"; @@ -60,11 +60,11 @@ class LoremIpsumGenerator extends Operation { case "Bytes": return GenerateBytes(length); default: - throw new OperationError("invalid lengthType"); + throw new OperationError("Invalid length type"); } } } -export default LoremIpsumGenerator; +export default GenerateLoremIpsum; From 324c409ff11c563fa8d4dce0ccef6460d4934d1d Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 9 Jan 2019 16:38:53 +0000 Subject: [PATCH 082/147] 8.20.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 81136cc8..fb28121d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.7", + "version": "8.20.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e1b801d2..bdc53f20 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.7", + "version": "8.20.0", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 995fcab071e187c092ee085bc6e5c41c817e9590 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Thu, 10 Jan 2019 15:01:01 +0000 Subject: [PATCH 083/147] Tidied up Case Insensitive Regex ops --- CHANGELOG.md | 6 ++++++ src/core/operations/FromCaseInsensitiveRegex.mjs | 2 +- src/core/operations/ToCaseInsensitiveRegex.mjs | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9b8eff0..4761b540 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ 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.21.0] - 2019-01-10 +- 'To Case Insensitive Regex' and 'From Case Insensitive Regex' operations added [@masq] | [#461] + ### [8.20.0] - 2019-01-09 - 'Generate Lorem Ipsum' operation added [@klaxon1] | [#455] @@ -91,6 +94,7 @@ All major and minor version changes will be documented in this file. Details of +[8.21.0]: https://github.com/gchq/CyberChef/releases/tag/v8.21.0 [8.20.0]: https://github.com/gchq/CyberChef/releases/tag/v8.20.0 [8.19.0]: https://github.com/gchq/CyberChef/releases/tag/v8.19.0 [8.18.0]: https://github.com/gchq/CyberChef/releases/tag/v8.18.0 @@ -134,6 +138,7 @@ All major and minor version changes will be documented in this file. Details of [@tcode2k16]: https://github.com/tcode2k16 [@Cynser]: https://github.com/Cynser [@anthony-arnold]: https://github.com/anthony-arnold +[@masq]: https://github.com/masq [#95]: https://github.com/gchq/CyberChef/pull/299 [#173]: https://github.com/gchq/CyberChef/pull/173 @@ -165,3 +170,4 @@ All major and minor version changes will be documented in this file. Details of [#449]: https://github.com/gchq/CyberChef/pull/449 [#455]: https://github.com/gchq/CyberChef/pull/455 [#458]: https://github.com/gchq/CyberChef/pull/458 +[#461]: https://github.com/gchq/CyberChef/pull/461 diff --git a/src/core/operations/FromCaseInsensitiveRegex.mjs b/src/core/operations/FromCaseInsensitiveRegex.mjs index 2448c5e5..36cd9b14 100644 --- a/src/core/operations/FromCaseInsensitiveRegex.mjs +++ b/src/core/operations/FromCaseInsensitiveRegex.mjs @@ -19,7 +19,7 @@ class FromCaseInsensitiveRegex extends Operation { this.name = "From Case Insensitive Regex"; this.module = "Default"; - this.description = "Converts a case-insensitive regex string to a case sensitive regex string (no guarantee on it being the proper original casing) in case /i wasn't available at the time but now is, or you need it to be case-sensitive again."; + this.description = "Converts a case-insensitive regex string to a case sensitive regex string (no guarantee on it being the proper original casing) in case the i flag wasn't available at the time but now is, or you need it to be case-sensitive again.

e.g. [mM][oO][zZ][iI][lL][lL][aA]/[0-9].[0-9] .* becomes Mozilla/[0-9].[0-9] .*"; this.infoURL = "https://wikipedia.org/wiki/Regular_expression"; this.inputType = "string"; this.outputType = "string"; diff --git a/src/core/operations/ToCaseInsensitiveRegex.mjs b/src/core/operations/ToCaseInsensitiveRegex.mjs index 32fb96c7..0d606a05 100644 --- a/src/core/operations/ToCaseInsensitiveRegex.mjs +++ b/src/core/operations/ToCaseInsensitiveRegex.mjs @@ -19,7 +19,7 @@ class ToCaseInsensitiveRegex extends Operation { this.name = "To Case Insensitive Regex"; this.module = "Default"; - this.description = "Converts a case-sensitive regex string into a case-insensitive regex string in case /i flag is unavailable to you."; + this.description = "Converts a case-sensitive regex string into a case-insensitive regex string in case the i flag is unavailable to you.

e.g. Mozilla/[0-9].[0-9] .* becomes [mM][oO][zZ][iI][lL][lL][aA]/[0-9].[0-9] .*"; this.infoURL = "https://wikipedia.org/wiki/Regular_expression"; this.inputType = "string"; this.outputType = "string"; From 863a5256255367889452fd33da3842e987714c09 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Thu, 10 Jan 2019 15:02:26 +0000 Subject: [PATCH 084/147] 8.21.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb28121d..9078d0c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.20.0", + "version": "8.21.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index bdc53f20..c0152ab0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.20.0", + "version": "8.21.0", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 9e63e40dab12874585a82445e3abf63dac6308b1 Mon Sep 17 00:00:00 2001 From: j433866 Date: Thu, 10 Jan 2019 15:24:29 +0000 Subject: [PATCH 085/147] Add new MGRS module and update webpack-dev-server --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 48751e21..92fda38b 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "url-loader": "^1.1.2", "web-resource-inliner": "^4.2.1", "webpack": "^4.25.1", - "webpack-dev-server": "^3.1.10", + "webpack-dev-server": "^3.1.14", "webpack-node-externals": "^1.7.2", "worker-loader": "^2.0.0" }, @@ -104,6 +104,7 @@ "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", + "mgrs": "^1.0.0", "moment": "^2.22.2", "moment-timezone": "^0.5.23", "ngeohash": "^0.6.0", From c2068b343b3ae7c767d341f6c798f0fda35f3c59 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Thu, 10 Jan 2019 15:42:48 +0000 Subject: [PATCH 086/147] Tidied up and added global matching to Subsection operation --- CHANGELOG.md | 5 +++++ src/core/operations/Subsection.mjs | 30 +++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4761b540..71949c82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ 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.22.0] - 2019-01-10 +- 'Subsection' operation added [@j433866] | [#467] + ### [8.21.0] - 2019-01-10 - 'To Case Insensitive Regex' and 'From Case Insensitive Regex' operations added [@masq] | [#461] @@ -94,6 +97,7 @@ All major and minor version changes will be documented in this file. Details of +[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.20.0]: https://github.com/gchq/CyberChef/releases/tag/v8.20.0 [8.19.0]: https://github.com/gchq/CyberChef/releases/tag/v8.19.0 @@ -171,3 +175,4 @@ All major and minor version changes will be documented in this file. Details of [#455]: https://github.com/gchq/CyberChef/pull/455 [#458]: https://github.com/gchq/CyberChef/pull/458 [#461]: https://github.com/gchq/CyberChef/pull/461 +[#467]: https://github.com/gchq/CyberChef/pull/467 diff --git a/src/core/operations/Subsection.mjs b/src/core/operations/Subsection.mjs index 94258b6d..8133d31c 100644 --- a/src/core/operations/Subsection.mjs +++ b/src/core/operations/Subsection.mjs @@ -38,6 +38,11 @@ class Subsection extends Operation { "type": "boolean", "value": true }, + { + "name": "Global matching", + "type": "boolean", + "value": true + }, { "name": "Ignore errors", "type": "boolean", @@ -49,7 +54,7 @@ class Subsection extends Operation { /** * @param {Object} state - The current state of the recipe. * @param {number} state.progress - The current position in the recipe. - * @param {Dish} state.Dish - The Dish being operated on + * @param {Dish} state.dish - The Dish being operated on * @param {Operation[]} state.opList - The list of operations in the recipe * @returns {Object} - The updated state of the recipe */ @@ -59,12 +64,12 @@ class Subsection extends Operation { outputType = opList[state.progress].outputType, input = await state.dish.get(inputType), ings = opList[state.progress].ingValues, - [section, caseSensitive, ignoreErrors] = ings, + [section, caseSensitive, global, ignoreErrors] = ings, subOpList = []; if (input && section !== "") { // Create subOpList for each tranche to operate on - // (all remaining operations unless we encounter a Merge) + // all remaining operations unless we encounter a Merge for (let i = state.progress + 1; i < opList.length; i++) { if (opList[i].name === "Merge" && !opList[i].disabled) { break; @@ -73,13 +78,15 @@ class Subsection extends Operation { } } - let flags = "g", + let flags = "", inOffset = 0, output = "", m, progress = 0; - if (!caseSensitive) - flags += "i"; + + if (!caseSensitive) flags += "i"; + if (global) flags += "g"; + const regex = new XRegExp(section, flags), recipe = new Recipe(); @@ -95,16 +102,19 @@ class Subsection extends Operation { matched = true; // Add up to match let matchStr = m[0]; - if (m.length === 1) { + + if (m.length === 1) { // No capture groups output += input.slice(inOffset, m.index); - inOffset = regex.lastIndex; + inOffset = m.index + m[0].length; } else if (m.length >= 2) { matchStr = m[1]; + // Need to add some of the matched string that isn't in the capture group output += input.slice(inOffset, m.index + m[0].indexOf(m[1])); // Set i to be after the end of the first capture group - inOffset = regex.lastIndex - (m[0].length - (m[0].indexOf(m[1]) + m[1].length)); + inOffset = m.index + m[0].indexOf(m[1]) + m[1].length; } + // Baseline ing values for each tranche so that registers are reset subOpList.forEach((op, i) => { op.ingValues = JSON.parse(JSON.stringify(ingValues[i])); @@ -122,7 +132,9 @@ class Subsection extends Operation { progress = err.progress + 1; } output += await dish.get(outputType); + if (!regex.global) break; } + // If no matches were found, advance progress to after a Merge op // Otherwise, the operations below Subsection will be run on all the input data if (!matched) { From 9787ab04cdc671b9cf1ea8f64306f136eaa9371a Mon Sep 17 00:00:00 2001 From: n1474335 Date: Thu, 10 Jan 2019 15:44:02 +0000 Subject: [PATCH 087/147] 8.22.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9078d0c3..00e57f64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.21.0", + "version": "8.22.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c0152ab0..96053213 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.21.0", + "version": "8.22.0", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From abdd70c6fa5f15dd53a6dfff9e5d379ef421d49e Mon Sep 17 00:00:00 2001 From: j433866 Date: Fri, 11 Jan 2019 11:59:13 +0000 Subject: [PATCH 088/147] Add ConvertCoordinates to lib folder --- src/core/lib/ConvertCoordinates.mjs | 210 ++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 src/core/lib/ConvertCoordinates.mjs diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs new file mode 100644 index 00000000..45ab0690 --- /dev/null +++ b/src/core/lib/ConvertCoordinates.mjs @@ -0,0 +1,210 @@ +/** + * Co-ordinate conversion resources. + * + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import geohash from "ngeohash"; +import mgrs from "mgrs"; + +/** + * Co-ordinate formats + */ +export const FORMATS = [ + "Degrees Minutes Seconds", + "Degrees Decimal Minutes", + "Decimal Degrees", + "Geohash", + "Military Grid Reference System" +]; + +/** + * Convert a given latitude and longitude into a different format. + * @param {string} inLat - Input latitude to be converted. Use this for supplying single values for conversion (e.g. geohash) + * @param {string} inLong - Input longitude to be converted + * @param {string} inFormat - Format of the input coordinates + * @param {string} outFormat - Format to convert to + * @param {number} precision - Precision of the result + * @returns {string[]} Array containing the converted latitude and longitude + */ +export function convertCoordinates (inLat, inLong, inFormat, outFormat, precision) { + let convLat = inLat; + let convLong = inLong; + if (inFormat === "Geohash") { + const hash = geohash.decode(inLat); + convLat = hash.latitude.toString(); + convLong = hash.longitude.toString(); + } else if (inFormat === "Military Grid Reference System") { + const result = mgrs.toPoint(inLat.replace(" ", "")); + convLat = result[1]; + convLong = result[0]; + } else { + convLat = convertSingleCoordinate(inLat, inFormat, "Decimal Degrees", 15).split("°"); + convLong = convertSingleCoordinate(inLong, inFormat, "Decimal Degrees", 15).split("°"); + } + + if (outFormat === "Geohash") { + convLat = geohash.encode(parseFloat(convLat), parseFloat(convLong), precision); + } else if (outFormat === "Military Grid Reference System") { + convLat = mgrs.forward([parseFloat(convLong), parseFloat(convLat)], precision); + } else { + convLat = convertSingleCoordinate(convLat.toString(), "Decimal Degrees", outFormat, precision); + convLong = convertSingleCoordinate(convLong.toString(), "Decimal Degrees", outFormat, precision); + } + + return [convLat, convLong]; +} + +/** + * @param {string} input - The input co-ordinate to be converted + * @param {string} inFormat - The format of the input co-ordinates + * @param {string} outFormat - The format which input should be converted to + * @param {boolean} returnRaw - When true, returns the raw float instead of a String + * @returns {string|{Object}} The converted co-ordinate result, as either the raw object or a formatted string + */ +export function convertSingleCoordinate (input, inFormat, outFormat, precision, returnRaw = false){ + let converted; + precision = Math.pow(10, precision); + const convData = splitInput(input); + // Convert everything to decimal degrees first + switch (inFormat) { + case "Degrees Minutes Seconds": + if (convData.length < 3) { + throw "Invalid co-ordinates format."; + } + converted = convDMSToDD(convData[0], convData[1], convData[2], precision); + break; + case "Degrees Decimal Minutes": + if (convData.length < 2) { + throw "Invalid co-ordinates format."; + } + converted = convDDMToDD(convData[0], convData[1], precision); + break; + case "Decimal Degrees": + if (convData.length < 1) { + throw "Invalid co-ordinates format."; + } + converted = convDDToDD(convData[0], precision); + break; + default: + throw "Unknown input format selection."; + } + + // Convert from decimal degrees to the output format + switch (outFormat) { + case "Decimal Degrees": + break; + case "Degrees Minutes Seconds": + converted = convDDToDMS(converted.degrees); + break; + case "Degrees Decimal Minutes": + converted = convDDToDDM(converted.degrees, precision); + break; + default: + throw "Unknown output format selection."; + } + if (returnRaw) { + return converted; + } else { + return converted.string; + } +} + +/** + * Split up the input using a space, and sanitise the result + * @param {string} input - The input data to be split + * @returns {number[]} An array of the different items in the string, stored as floats + */ +function splitInput (input){ + const split = []; + + input.split(" ").forEach(item => { + // Remove any character that isn't a digit + item = item.replace(/[^0-9.-]/g, ""); + if (item.length > 0){ + split.push(parseFloat(item, 10)); + } + }); + return split; +} + +/** + * Convert Degrees Minutes Seconds to Decimal Degrees + * @param {number} degrees - The degrees of the input co-ordinates + * @param {number} minutes - The minutes of the input co-ordinates + * @param {number} seconds - The seconds of the input co-ordinates + * @param {number} precision - The precision the result should be rounded to + * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) + */ +function convDMSToDD (degrees, minutes, seconds, precision){ + const converted = new Object(); + converted.degrees = degrees + (minutes / 60) + (seconds / 3600); + converted.string = (Math.round(converted.degrees * precision) / precision) + "°"; + return converted; +} + +/** + * Convert Decimal Degrees Minutes to Decimal Degrees + * @param {number} degrees - The input degrees to be converted + * @param {number} minutes - The input minutes to be converted + * @param {number} precision - The precision which the result should be rounded to + * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) + */ +function convDDMToDD (degrees, minutes, precision) { + const converted = new Object(); + converted.degrees = degrees + minutes / 60; + converted.string = ((Math.round(converted.degrees * precision) / precision) + "°"); + return converted; +} + +/** + * Convert Decimal Degrees to Decimal Degrees + * Doesn't affect the input, just puts it into an object + * @param {number} degrees - The input degrees to be converted + * @param {number} precision - The precision which the result should be rounded to + * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) + */ +function convDDToDD (degrees, precision) { + const converted = new Object(); + converted.degrees = degrees; + converted.string = Math.round(converted.degrees * precision) / precision + "°"; + return converted; +} + +/** + * Convert Decimal Degrees to Degrees Minutes Seconds + * @param {number} decDegrees - The input data to be converted + * @returns {{string: string, degrees: number, minutes: number, seconds: number}} An object containing the raw converted value as separate numbers (.degrees, .minutes, .seconds), and a formatted string version (obj.string) + */ +function convDDToDMS (decDegrees) { + const degrees = Math.floor(decDegrees); + const minutes = Math.floor(60 * (decDegrees - degrees)); + const seconds = Math.round(3600 * (decDegrees - degrees) - 60 * minutes); + + const converted = new Object(); + converted.degrees = degrees; + converted.minutes = minutes; + converted.seconds = seconds; + converted.string = degrees + "° " + minutes + "' " + seconds + "\""; + return converted; +} + +/** + * Convert Decimal Degrees to Degrees Decimal Minutes + * @param {number} decDegrees - The input degrees to be converted + * @param {number} precision - The precision the input data should be rounded to + * @returns {{string: string, degrees: number, minutes: number}} An object containing the raw converted value as separate numbers (.degrees, .minutes), and a formatted string version (obj.string) + */ +function convDDToDDM (decDegrees, precision) { + const degrees = Math.floor(decDegrees); + const minutes = decDegrees - degrees; + const decMinutes = Math.round((minutes * 60) * precision) / precision; + + const converted = new Object(); + converted.degrees = degrees; + converted.minutes = decMinutes; + converted.string = degrees + "° " + decMinutes + "'"; + return converted; +} From 8bba4b297391377eaa0dcd527353742a3f7a0e5e Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 12 Jan 2019 00:20:25 +0000 Subject: [PATCH 089/147] More speedrun stats (literally 10x faster) --- package-lock.json | 6 +++--- package.json | 2 +- src/core/operations/YARARules.mjs | 19 ++++++++++++------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b4d058b..6ba03db3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7756,9 +7756,9 @@ "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, "libyara-wasm": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.8.tgz", - "integrity": "sha512-ZB+Ya3bEBoanvde47X8RzqpMBHgrPxrTZIJ/UEoatVnOEy2he1IORuotdSkP2o73URRzHGN1jNWDIhTdfbZ3rQ==" + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.9.tgz", + "integrity": "sha512-GTR8GD7q2xCwRzQpoSVh31KPbH145tCKaU3DAnQMpkf4aKqjnVpQlq6gUjyDhvw8RPZ2tamyQad5hg42KsVvRw==" }, "livereload-js": { "version": "2.4.0", diff --git a/package.json b/package.json index 8e21a0a6..a18331d8 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", - "libyara-wasm": "0.0.8", + "libyara-wasm": "0.0.9", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", diff --git a/src/core/operations/YARARules.mjs b/src/core/operations/YARARules.mjs index 094d9f43..58b3576c 100644 --- a/src/core/operations/YARARules.mjs +++ b/src/core/operations/YARARules.mjs @@ -59,16 +59,22 @@ class YARARules extends Operation { * @returns {string} */ run(input, args) { + if (ENVIRONMENT_IS_WORKER()) + self.sendStatusMessage("Instantiating YARA."); const [rules, showStrings, showLengths, showMeta] = args; return new Promise((resolve, reject) => { Yara().then(yara => { + if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Converting data for YARA."); let matchString = ""; - const inpArr = new Uint8Array(input); - const inpVec = new yara.vectorChar(); - for (let i = 0; i < inpArr.length; i++) { - inpVec.push_back(inpArr[i]); - } - const resp = yara.run(inpVec, rules); + + const inpArr = new Uint8Array(input); // Turns out embind knows that JS uint8array <==> C++ std::string + + if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Running YARA matching."); + + const resp = yara.run(inpArr, rules); + + if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Processing data."); + if (resp.compileErrors.size() > 0) { for (let i = 0; i < resp.compileErrors.size(); i++) { const compileError = resp.compileErrors.get(i); @@ -102,7 +108,6 @@ class YARARules extends Operation { } } } - } resolve(matchString); }); From 68fbbb64dba87ef1d7edf2138870e8b98aa3e9d8 Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 11:49:57 +0000 Subject: [PATCH 090/147] Add new Convert co-ordinate format module. Also added autodetect of co-ordinate format / delimiter --- src/core/config/Categories.json | 1 + src/core/lib/ConvertCoordinates.mjs | 95 ++++++++ .../operations/ConvertCoordinateFormat.mjs | 227 ++++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 src/core/operations/ConvertCoordinateFormat.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index e9fe3399..13ea76c9 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -210,6 +210,7 @@ "Convert mass", "Convert speed", "Convert data units", + "Convert co-ordinate format", "Parse UNIX file permissions", "Swap endianness", "Parse colour code", diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index 45ab0690..a9b3d551 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -208,3 +208,98 @@ function convDDToDDM (decDegrees, precision) { converted.string = degrees + "° " + decMinutes + "'"; return converted; } + +/** + * + * @param {string} input - The input data whose format we need to detect + * @param {string} delim - The delimiter separating the data in input + * @returns {string} The input format + */ +export function findFormat (input, delim) { + input = input.trim(); + let testData; + if (delim.includes("Direction")) { + const split = input.split(/[NnEeSsWw]/); + if (split.length > 0) { + if (split[0] === "") { + // Direction Preceding + testData = split[1]; + } else { + // Direction Following + testData = split[0]; + } + } + } else if (delim !== "") { + const split = input.split(delim); + if (!input.includes(delim)) { + testData = input; + } + if (split.length > 0) { + if (split[0] !== "") { + testData = split[0]; + } else if (split.length > 1) { + testData = split[1]; + } + } + } + + // Test MGRS and Geohash + if (input.split(" ").length === 1) { + const mgrsPattern = new RegExp(/^[0-9]{2}[C-HJ-NP-X]{2}[A-Z]+/); + const geohashPattern = new RegExp(/^[0123456789bcdefghjkmnpqrstuvwxyz]+$/); + if (mgrsPattern.test(input.toUpperCase())) { + return "Military Grid Reference System"; + } else if (geohashPattern.test(input.toLowerCase())) { + return "Geohash"; + } + } + + // Test DMS/DDM/DD formats + if (testData !== undefined) { + const split = splitInput(testData); + if (split.length === 3) { + // DMS + return "Degrees Minutes Seconds"; + } else if (split.length === 2) { + // DDM + return "Degrees Decimal Minutes"; + } else if (split.length === 1) { + return "Decimal Degrees"; + } + } + return null; +} + +/** + * Automatically find the delimeter type from the given input + * @param {string} input + * @returns {string} Delimiter type + */ +export function findDelim (input) { + input = input.trim(); + const delims = [",", ";", ":"]; + // Direction + const testDir = input.match(/[NnEeSsWw]/g); + if (testDir !== null && testDir.length > 0 && testDir.length < 3) { + // Possible direction + const splitInput = input.split(/[NnEeSsWw]/); + if (splitInput.length <= 3 && splitInput.length > 0) { + // One of the splits should be an empty string + if (splitInput[0] === "") { + return "Direction Preceding"; + } else if (splitInput[splitInput.length - 1] === "") { + return "Direction Following"; + } + } + } + for (let i = 0; i < delims.length; i++) { + const delim = delims[i]; + if (input.includes(delim)) { + const splitInput = input.split(delim); + if (splitInput.length <= 3 && splitInput.length > 0) { + return delim; + } + } + } + return null; +} diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs new file mode 100644 index 00000000..5f336630 --- /dev/null +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -0,0 +1,227 @@ +/** + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import {FORMATS, convertCoordinates, convertSingleCoordinate, findDelim, findFormat} from "../lib/ConvertCoordinates"; +import Utils from "../Utils"; + +/** + * Convert co-ordinate format operation + */ +class ConvertCoordinateFormat extends Operation { + + /** + * ConvertCoordinateFormat constructor + */ + constructor() { + super(); + + this.name = "Convert co-ordinate format"; + this.module = "Hashing"; + this.description = "Convert geographical coordinates between different formats.

Currently supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
"; + this.infoURL = "https://wikipedia.org/wiki/Geographic_coordinate_conversion"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Input Format", + "type": "option", + "value": ["Auto"].concat(FORMATS) + }, + { + "name": "Input Delimiter", + "type": "option", + "value": [ + "Auto", + "Direction Preceding", // Need better names + "Direction Following", + "\\n", + "Comma", + "Semi-colon", + "Colon" + ] + }, + { + "name": "Output Format", + "type": "option", + "value": FORMATS + }, + { + "name": "Output Delimiter", + "type": "option", + "value": [ + "Space", + "Direction Preceding", // Need better names + "Direction Following", + "\\n", + "Comma", + "Semi-colon", + "Colon" + ] + }, + { + "name": "Precision", + "type": "number", + "value": 3 + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const outFormat = args[2], + outDelim = args[3], + precision = args[4]; + let inFormat = args[0], + inDelim = args[1], + inLat, + inLong, + outLat, + outLong, + latDir = "", + longDir = "", + outSeparator = " "; + + // Autodetect input delimiter + if (inDelim === "Auto") { + inDelim = findDelim(input); + log.error("DATA: " + input + " DELIM: " + inDelim); + if (inDelim === null) { + inDelim = ""; + // throw new OperationError("Could not automatically detect the input delimiter."); + } + } else if (!inDelim.includes("Direction")) { + // Get the actual delimiter from the regex + inDelim = String(Utils.regexRep(inDelim)).slice(1, 2); + } + if (inFormat === "Auto") { + inFormat = findFormat(input, inDelim); + log.error("DATA: " + input + " FORMAT: " + inFormat); + if (inFormat === null) { + throw new OperationError("Could not automatically detect the input"); + } + } + + if (inDelim === "" && (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System")) { + throw new OperationError("Could not automatically detect the input delimiter."); + } + + // Prepare input data + if (inFormat === "Geohash" || inFormat === "Military Grid Reference System") { + // Geohash only has one value, so just use the input + inLat = input; + } else if (inDelim === "Direction Preceding") { + // Split on the compass directions + const splitInput = input.split(/[NnEeSsWw]/); + const dir = input.match(/[NnEeSsWw]/g); + if (splitInput.length > 1) { + inLat = splitInput[1]; + if (dir !== null) { + latDir = dir[0]; + } + if (splitInput.length > 2) { + inLong = splitInput[2]; + if (dir !== null && dir.length > 1) { + longDir = dir[1]; + } + } + } + } else if (inDelim === "Direction Following") { + // Split on the compass directions + const splitInput = input.split(/[NnEeSsWw]/); + if (splitInput.length >= 1) { + inLat = splitInput[0]; + if (splitInput.length >= 2) { + inLong = splitInput[1]; + } + } + } else { + // Split on the delimiter + const splitInput = input.split(inDelim); + log.error(splitInput); + if (splitInput.length > 0) { + inLat = splitInput[0]; + if (splitInput.length >= 2) { + inLong = splitInput[1]; + } + } + } + + if (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System" && outDelim.includes("Direction")) { + // Match on compass directions, and store the first 2 matches for the output + const dir = input.match(/[NnEeSsWw]/g); + if (dir !== null) { + latDir = dir[0]; + if (dir.length > 1) { + longDir = dir[1]; + } + } + } else if (outDelim === "\\n") { + outSeparator = "\n"; + } else if (outDelim === "Space") { + outSeparator = " "; + } else if (!outDelim.includes("Direction")) { + // Cut out the regex syntax (/) from the delimiter + outSeparator = String(Utils.regexRep(outDelim)).slice(1, 2); + } + + // Convert the co-ordinates + if (inLat !== undefined) { + if (inLong === undefined) { + if (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System") { + if (outFormat === "Geohash" || outFormat === "Military Grid Reference System"){ + throw new OperationError(`${outFormat} needs both a latitude and a longitude to be calculated`); + } + } + if (inFormat === "Geohash" || inFormat === "Military Grid Reference System") { + // Geohash conversion is in convertCoordinates despite needing + // only one input as it needs to output two values + [outLat, outLong] = convertCoordinates(inLat, inLat, inFormat, outFormat, precision); + } else { + outLat = convertSingleCoordinate(inLat, inFormat, outFormat, precision); + } + } else { + [outLat, outLong] = convertCoordinates(inLat, inLong, inFormat, outFormat, precision); + } + } else { + throw new OperationError("No co-ordinates were detected in the input."); + } + + // Output conversion results if successful + if (outLat !== undefined) { + let output = ""; + if (outDelim === "Direction Preceding" && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + output += latDir += " "; + } + output += outLat; + if (outDelim === "Direction Following" && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + output += " " + latDir; + } + output += outSeparator; + + if (outLong !== undefined && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + if (outDelim === "Direction Preceding") { + output += longDir + " "; + } + output += outLong; + if (outDelim === "Direction Following") { + output += " " + longDir; + } + output += outSeparator; + } + return output; + } else { + throw new OperationError("Co-ordinate conversion failed."); + } + } +} + +export default ConvertCoordinateFormat; From 8d1f668fc550f5795a02f5f157411ffe45da89e0 Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 11:56:27 +0000 Subject: [PATCH 091/147] Remove old Geohash modules --- src/core/config/Categories.json | 4 +-- src/core/operations/FromGeohash.mjs | 44 ------------------------ src/core/operations/ToGeohash.mjs | 53 ----------------------------- 3 files changed, 1 insertion(+), 100 deletions(-) delete mode 100644 src/core/operations/FromGeohash.mjs delete mode 100644 src/core/operations/ToGeohash.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 13ea76c9..2224050c 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -302,9 +302,7 @@ "Adler-32 Checksum", "CRC-16 Checksum", "CRC-32 Checksum", - "TCP/IP Checksum", - "To Geohash", - "From Geohash" + "TCP/IP Checksum" ] }, { diff --git a/src/core/operations/FromGeohash.mjs b/src/core/operations/FromGeohash.mjs deleted file mode 100644 index b70273da..00000000 --- a/src/core/operations/FromGeohash.mjs +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @author gchq77703 [] - * @copyright Crown Copyright 2018 - * @license Apache-2.0 - */ - -import Operation from "../Operation"; -import geohash from "ngeohash"; - -/** - * From Geohash operation - */ -class FromGeohash extends Operation { - - /** - * FromGeohash constructor - */ - constructor() { - super(); - - this.name = "From Geohash"; - this.module = "Hashing"; - this.description = "Converts Geohash strings into Lat/Long coordinates. For example, ww8p1r4t8 becomes 37.8324,112.5584."; - this.infoURL = "https://wikipedia.org/wiki/Geohash"; - this.inputType = "string"; - this.outputType = "string"; - this.args = []; - } - - /** - * @param {string} input - * @param {Object[]} args - * @returns {string} - */ - run(input, args) { - return input.split("\n").map(line => { - const coords = geohash.decode(line); - return [coords.latitude, coords.longitude].join(","); - }).join("\n"); - } - -} - -export default FromGeohash; diff --git a/src/core/operations/ToGeohash.mjs b/src/core/operations/ToGeohash.mjs deleted file mode 100644 index 0e7f53ac..00000000 --- a/src/core/operations/ToGeohash.mjs +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @author gchq77703 [] - * @copyright Crown Copyright 2018 - * @license Apache-2.0 - */ - -import Operation from "../Operation"; -import geohash from "ngeohash"; - -/** - * To Geohash operation - */ -class ToGeohash extends Operation { - - /** - * ToGeohash constructor - */ - constructor() { - super(); - - this.name = "To Geohash"; - this.module = "Hashing"; - this.description = "Converts Lat/Long coordinates into a Geohash string. For example, 37.8324,112.5584 becomes ww8p1r4t8."; - this.infoURL = "https://wikipedia.org/wiki/Geohash"; - this.inputType = "string"; - this.outputType = "string"; - this.args = [ - { - name: "Precision", - type: "number", - value: 9 - } - ]; - } - - /** - * @param {string} input - * @param {Object[]} args - * @returns {string} - */ - run(input, args) { - const [precision] = args; - - return input.split("\n").map(line => { - line = line.replace(/ /g, ""); - if (line === "") return ""; - return geohash.encode(...line.split(",").map(num => parseFloat(num)), precision); - }).join("\n"); - } - -} - -export default ToGeohash; From 8b77ad77480b7cf1450e32d2c63dc17f0a6f46ee Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 12:49:28 +0000 Subject: [PATCH 092/147] Stop delimiters breaking MGRS conversion --- src/core/operations/ConvertCoordinateFormat.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index 5f336630..d24b2135 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -117,7 +117,8 @@ class ConvertCoordinateFormat extends Operation { // Prepare input data if (inFormat === "Geohash" || inFormat === "Military Grid Reference System") { // Geohash only has one value, so just use the input - inLat = input; + // Replace anything that isn't a valid character in Geohash / MGRS + inLat = input.replace(/[^A-Za-z0-9]/, ""); } else if (inDelim === "Direction Preceding") { // Split on the compass directions const splitInput = input.split(/[NnEeSsWw]/); From 1a88a0164cf8d27d260afc9e8b8b72d5db39fe3a Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 13:00:14 +0000 Subject: [PATCH 093/147] Fix delimiter breaking Geohash detection --- src/core/lib/ConvertCoordinates.mjs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index a9b3d551..9b9d8881 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -45,6 +45,7 @@ export function convertCoordinates (inLat, inLong, inFormat, outFormat, precisio convLong = convertSingleCoordinate(inLong, inFormat, "Decimal Degrees", 15).split("°"); } + // Convert Geohash and MGRS here, as they need both the lat and long values if (outFormat === "Geohash") { convLat = geohash.encode(parseFloat(convLat), parseFloat(convLong), precision); } else if (outFormat === "Military Grid Reference System") { @@ -244,12 +245,14 @@ export function findFormat (input, delim) { } // Test MGRS and Geohash - if (input.split(" ").length === 1) { + if (input.split(" ").length <= 1) { + const filteredInput = input.replace(/[^A-Za-z0-9]/, "").toUpperCase(); const mgrsPattern = new RegExp(/^[0-9]{2}[C-HJ-NP-X]{2}[A-Z]+/); - const geohashPattern = new RegExp(/^[0123456789bcdefghjkmnpqrstuvwxyz]+$/); - if (mgrsPattern.test(input.toUpperCase())) { + const geohashPattern = new RegExp(/^[0123456789BCDEFGHJKMNPQRSTUVWXYZ]+$/); + log.error(filteredInput); + if (mgrsPattern.test(filteredInput)) { return "Military Grid Reference System"; - } else if (geohashPattern.test(input.toLowerCase())) { + } else if (geohashPattern.test(filteredInput)) { return "Geohash"; } } @@ -292,6 +295,8 @@ export function findDelim (input) { } } } + + // Loop through the standard delimiters, and try to find them in the input for (let i = 0; i < delims.length; i++) { const delim = delims[i]; if (input.includes(delim)) { From b3ac8d0835e90c9301cfd86f58d2f619a1d246c6 Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 13:49:49 +0000 Subject: [PATCH 094/147] Removed some debug logging --- src/core/lib/ConvertCoordinates.mjs | 1 - src/core/operations/ConvertCoordinateFormat.mjs | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index 9b9d8881..0078cfb7 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -249,7 +249,6 @@ export function findFormat (input, delim) { const filteredInput = input.replace(/[^A-Za-z0-9]/, "").toUpperCase(); const mgrsPattern = new RegExp(/^[0-9]{2}[C-HJ-NP-X]{2}[A-Z]+/); const geohashPattern = new RegExp(/^[0123456789BCDEFGHJKMNPQRSTUVWXYZ]+$/); - log.error(filteredInput); if (mgrsPattern.test(filteredInput)) { return "Military Grid Reference System"; } else if (geohashPattern.test(filteredInput)) { diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index d24b2135..7528fccc 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -22,7 +22,7 @@ class ConvertCoordinateFormat extends Operation { this.name = "Convert co-ordinate format"; this.module = "Hashing"; - this.description = "Convert geographical coordinates between different formats.

Currently supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
"; + this.description = "Convert geographical coordinates between different formats.

Supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
"; this.infoURL = "https://wikipedia.org/wiki/Geographic_coordinate_conversion"; this.inputType = "string"; this.outputType = "string"; @@ -93,10 +93,8 @@ class ConvertCoordinateFormat extends Operation { // Autodetect input delimiter if (inDelim === "Auto") { inDelim = findDelim(input); - log.error("DATA: " + input + " DELIM: " + inDelim); if (inDelim === null) { inDelim = ""; - // throw new OperationError("Could not automatically detect the input delimiter."); } } else if (!inDelim.includes("Direction")) { // Get the actual delimiter from the regex @@ -104,9 +102,8 @@ class ConvertCoordinateFormat extends Operation { } if (inFormat === "Auto") { inFormat = findFormat(input, inDelim); - log.error("DATA: " + input + " FORMAT: " + inFormat); if (inFormat === null) { - throw new OperationError("Could not automatically detect the input"); + throw new OperationError("Could not automatically detect the input format."); } } @@ -147,7 +144,6 @@ class ConvertCoordinateFormat extends Operation { } else { // Split on the delimiter const splitInput = input.split(inDelim); - log.error(splitInput); if (splitInput.length > 0) { inLat = splitInput[0]; if (splitInput.length >= 2) { From 04b0b8c72344aa136dd2cd879fc99f2ffb678ec5 Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 14:58:41 +0000 Subject: [PATCH 095/147] Tidy up code --- src/core/lib/ConvertCoordinates.mjs | 21 +++++++++---------- .../operations/ConvertCoordinateFormat.mjs | 4 ++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index 0078cfb7..1c6ae7e2 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -259,14 +259,13 @@ export function findFormat (input, delim) { // Test DMS/DDM/DD formats if (testData !== undefined) { const split = splitInput(testData); - if (split.length === 3) { - // DMS - return "Degrees Minutes Seconds"; - } else if (split.length === 2) { - // DDM - return "Degrees Decimal Minutes"; - } else if (split.length === 1) { - return "Decimal Degrees"; + switch (split.length){ + case 3: + return "Degrees Minutes Seconds"; + case 2: + return "Degrees Decimal Minutes"; + case 1: + return "Decimal Degrees"; } } return null; @@ -280,13 +279,12 @@ export function findFormat (input, delim) { export function findDelim (input) { input = input.trim(); const delims = [",", ";", ":"]; - // Direction const testDir = input.match(/[NnEeSsWw]/g); if (testDir !== null && testDir.length > 0 && testDir.length < 3) { - // Possible direction + // Possibly contains a direction const splitInput = input.split(/[NnEeSsWw]/); if (splitInput.length <= 3 && splitInput.length > 0) { - // One of the splits should be an empty string + // If there's 3 splits (one should be empty), then assume we have directions if (splitInput[0] === "") { return "Direction Preceding"; } else if (splitInput[splitInput.length - 1] === "") { @@ -301,6 +299,7 @@ export function findDelim (input) { if (input.includes(delim)) { const splitInput = input.split(delim); if (splitInput.length <= 3 && splitInput.length > 0) { + // Don't want to try and convert more than 2 co-ordinates return delim; } } diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index 7528fccc..b9a9766c 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -37,7 +37,7 @@ class ConvertCoordinateFormat extends Operation { "type": "option", "value": [ "Auto", - "Direction Preceding", // Need better names + "Direction Preceding", "Direction Following", "\\n", "Comma", @@ -55,7 +55,7 @@ class ConvertCoordinateFormat extends Operation { "type": "option", "value": [ "Space", - "Direction Preceding", // Need better names + "Direction Preceding", "Direction Following", "\\n", "Comma", From ee360521bb757c9cae9010b13db0932a46aa1e02 Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 16:41:06 +0000 Subject: [PATCH 096/147] Remove MGRS npm module --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 92fda38b..414e4d50 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,6 @@ "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", - "mgrs": "^1.0.0", "moment": "^2.22.2", "moment-timezone": "^0.5.23", "ngeohash": "^0.6.0", From 4d8127a7d98b18309d5d4bc44892627f647adb23 Mon Sep 17 00:00:00 2001 From: Callum Fraser Date: Mon, 14 Jan 2019 22:24:32 +0000 Subject: [PATCH 097/147] Modified description of ToBase64 operation Addresses #472 --- src/core/operations/ToBase64.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/ToBase64.mjs b/src/core/operations/ToBase64.mjs index f9aa569b..cffc3140 100644 --- a/src/core/operations/ToBase64.mjs +++ b/src/core/operations/ToBase64.mjs @@ -20,7 +20,7 @@ class ToBase64 extends Operation { this.name = "To Base64"; this.module = "Default"; - this.description = "Base64 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.

This operation decodes data from an ASCII Base64 string back into its raw format.

e.g. aGVsbG8= becomes hello"; + this.description = "Base64 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.

This operation encodes raw data into an ASCII Base64 string.

e.g. hello becomes aGVsbG8="; this.infoURL = "https://wikipedia.org/wiki/Base64"; this.inputType = "ArrayBuffer"; this.outputType = "string"; From ad4451a757f0650ee492c41a89014b36ead8993b Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 15 Jan 2019 10:13:11 +0000 Subject: [PATCH 098/147] Rewrite MGRS to use new Geodesy module. Added Ordnance Survey grid reference support --- package.json | 1 + src/core/lib/ConvertCoordinates.mjs | 43 ++++++++++++++++--- .../operations/ConvertCoordinateFormat.mjs | 25 +++++------ 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 414e4d50..4e38e4fa 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "esprima": "^4.0.1", "exif-parser": "^0.1.12", "file-saver": "^2.0.0-rc.4", + "geodesy": "^1.1.3", "highlight.js": "^9.13.1", "jquery": "^3.3.1", "js-crc": "^0.2.0", diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index 1c6ae7e2..06e40f02 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -7,7 +7,7 @@ */ import geohash from "ngeohash"; -import mgrs from "mgrs"; +import geodesy from "geodesy"; /** * Co-ordinate formats @@ -17,7 +17,19 @@ export const FORMATS = [ "Degrees Decimal Minutes", "Decimal Degrees", "Geohash", - "Military Grid Reference System" + "Military Grid Reference System", + "Ordnance Survey National Grid" +]; + +/** + * Formats that are made up of one string + * These formats skip bits like filtering delimiters and + * are outputted differently (only one output) + */ +export const STRING_FORMATS = [ + "Geohash", + "Military Grid Reference System", + "Ordnance Survey National Grid" ]; /** @@ -37,9 +49,22 @@ export function convertCoordinates (inLat, inLong, inFormat, outFormat, precisio convLat = hash.latitude.toString(); convLong = hash.longitude.toString(); } else if (inFormat === "Military Grid Reference System") { - const result = mgrs.toPoint(inLat.replace(" ", "")); - convLat = result[1]; - convLong = result[0]; + const utm = geodesy.Mgrs.parse(inLat).toUtm(); + const result = utm.toLatLonE().toString("d", 4).replace(/[^0-9.,]/g, ""); + const splitResult = result.split(","); + if (splitResult.length === 2) { + convLat = splitResult[0]; + convLong = splitResult[1]; + } + } else if (inFormat === "Ordnance Survey National Grid") { + const osng = geodesy.OsGridRef.parse(inLat); + const latlon = geodesy.OsGridRef.osGridToLatLon(osng, geodesy.LatLonEllipsoidal.datum.WGS84); + const result = latlon.toString("d", 4).replace(/[^0-9.,]/g, ""); + const splitResult = result.split(","); + if (splitResult.length === 2) { + convLat = splitResult[0]; + convLong = splitResult[1]; + } } else { convLat = convertSingleCoordinate(inLat, inFormat, "Decimal Degrees", 15).split("°"); convLong = convertSingleCoordinate(inLong, inFormat, "Decimal Degrees", 15).split("°"); @@ -49,7 +74,13 @@ export function convertCoordinates (inLat, inLong, inFormat, outFormat, precisio if (outFormat === "Geohash") { convLat = geohash.encode(parseFloat(convLat), parseFloat(convLong), precision); } else if (outFormat === "Military Grid Reference System") { - convLat = mgrs.forward([parseFloat(convLong), parseFloat(convLat)], precision); + const utm = new geodesy.LatLonEllipsoidal(parseFloat(convLat), parseFloat(convLong)).toUtm(); + const mgrs = utm.toMgrs(); + convLat = mgrs.toString(); + } else if (outFormat === "Ordnance Survey National Grid") { + const latlon = new geodesy.LatLonEllipsoidal(parseFloat(convLat), parseFloat(convLong)); + const osng = geodesy.OsGridRef.latLonToOsGrid(latlon); + convLat = osng.toString(); } else { convLat = convertSingleCoordinate(convLat.toString(), "Decimal Degrees", outFormat, precision); convLong = convertSingleCoordinate(convLong.toString(), "Decimal Degrees", outFormat, precision); diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index b9a9766c..afc95982 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -6,7 +6,7 @@ import Operation from "../Operation"; import OperationError from "../errors/OperationError"; -import {FORMATS, convertCoordinates, convertSingleCoordinate, findDelim, findFormat} from "../lib/ConvertCoordinates"; +import {FORMATS, STRING_FORMATS, convertCoordinates, convertSingleCoordinate, findDelim, findFormat} from "../lib/ConvertCoordinates"; import Utils from "../Utils"; /** @@ -22,7 +22,7 @@ class ConvertCoordinateFormat extends Operation { this.name = "Convert co-ordinate format"; this.module = "Hashing"; - this.description = "Convert geographical coordinates between different formats.

Supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
"; + this.description = "Convert geographical coordinates between different formats.

Supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
  • Ordnance Survey National Grid (OSNG)
"; this.infoURL = "https://wikipedia.org/wiki/Geographic_coordinate_conversion"; this.inputType = "string"; this.outputType = "string"; @@ -107,14 +107,14 @@ class ConvertCoordinateFormat extends Operation { } } - if (inDelim === "" && (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System")) { + if (inDelim === "" && (!STRING_FORMATS.includes(inFormat))) { throw new OperationError("Could not automatically detect the input delimiter."); } // Prepare input data - if (inFormat === "Geohash" || inFormat === "Military Grid Reference System") { + if (STRING_FORMATS.includes(inFormat)) { // Geohash only has one value, so just use the input - // Replace anything that isn't a valid character in Geohash / MGRS + // Replace anything that isn't a valid character in Geohash / MGRS / OSNG inLat = input.replace(/[^A-Za-z0-9]/, ""); } else if (inDelim === "Direction Preceding") { // Split on the compass directions @@ -152,7 +152,7 @@ class ConvertCoordinateFormat extends Operation { } } - if (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System" && outDelim.includes("Direction")) { + if (!STRING_FORMATS.includes(inFormat) && outDelim.includes("Direction")) { // Match on compass directions, and store the first 2 matches for the output const dir = input.match(/[NnEeSsWw]/g); if (dir !== null) { @@ -173,14 +173,15 @@ class ConvertCoordinateFormat extends Operation { // Convert the co-ordinates if (inLat !== undefined) { if (inLong === undefined) { - if (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System") { - if (outFormat === "Geohash" || outFormat === "Military Grid Reference System"){ + if (!STRING_FORMATS.includes(inFormat)) { + if (STRING_FORMATS.includes(outFormat)){ throw new OperationError(`${outFormat} needs both a latitude and a longitude to be calculated`); } } - if (inFormat === "Geohash" || inFormat === "Military Grid Reference System") { + if (STRING_FORMATS.includes(inFormat)) { // Geohash conversion is in convertCoordinates despite needing // only one input as it needs to output two values + inLat = inLat.replace(/[^A-Za-z0-9]/g, ""); [outLat, outLong] = convertCoordinates(inLat, inLat, inFormat, outFormat, precision); } else { outLat = convertSingleCoordinate(inLat, inFormat, outFormat, precision); @@ -195,16 +196,16 @@ class ConvertCoordinateFormat extends Operation { // Output conversion results if successful if (outLat !== undefined) { let output = ""; - if (outDelim === "Direction Preceding" && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + if (outDelim === "Direction Preceding" && !STRING_FORMATS.includes(outFormat)) { output += latDir += " "; } output += outLat; - if (outDelim === "Direction Following" && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + if (outDelim === "Direction Following" && !STRING_FORMATS.includes(outFormat)) { output += " " + latDir; } output += outSeparator; - if (outLong !== undefined && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + if (outLong !== undefined && !STRING_FORMATS.includes(outFormat)) { if (outDelim === "Direction Preceding") { output += longDir + " "; } From 5e68959c03cc460edd93d7dbe9444d8250958711 Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 15 Jan 2019 10:25:49 +0000 Subject: [PATCH 099/147] Catch when OS grid references aren't calculated --- src/core/lib/ConvertCoordinates.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index 06e40f02..b6f9e9bf 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -81,6 +81,9 @@ export function convertCoordinates (inLat, inLong, inFormat, outFormat, precisio const latlon = new geodesy.LatLonEllipsoidal(parseFloat(convLat), parseFloat(convLong)); const osng = geodesy.OsGridRef.latLonToOsGrid(latlon); convLat = osng.toString(); + if (convLat === "") { + throw "Couldn't convert co-ordinates to Ordnance Survey National Grid. Are they out of range?"; + } } else { convLat = convertSingleCoordinate(convLat.toString(), "Decimal Degrees", outFormat, precision); convLong = convertSingleCoordinate(convLong.toString(), "Decimal Degrees", outFormat, precision); From d00b0f4c0e9e06af134de7cf680425b2be3668d9 Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 15 Jan 2019 15:55:49 +0000 Subject: [PATCH 100/147] Basically rewrote the whole thing using the new geodesy module --- src/core/lib/ConvertCoordinates.mjs | 464 +++++++++++++----- .../operations/ConvertCoordinateFormat.mjs | 162 +----- 2 files changed, 353 insertions(+), 273 deletions(-) diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index b6f9e9bf..cec2439c 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -18,148 +18,240 @@ export const FORMATS = [ "Decimal Degrees", "Geohash", "Military Grid Reference System", - "Ordnance Survey National Grid" + "Ordnance Survey National Grid", + "Universal Transverse Mercator" ]; /** - * Formats that are made up of one string - * These formats skip bits like filtering delimiters and - * are outputted differently (only one output) + * Formats that should be passed to Geodesy module as-is + * Spaces are still removed */ -export const STRING_FORMATS = [ +const NO_CHANGE = [ "Geohash", "Military Grid Reference System", - "Ordnance Survey National Grid" + "Ordnance Survey National Grid", + "Universal Transverse Mercator", ]; /** * Convert a given latitude and longitude into a different format. - * @param {string} inLat - Input latitude to be converted. Use this for supplying single values for conversion (e.g. geohash) - * @param {string} inLong - Input longitude to be converted + * @param {string} input - Input string to be converted * @param {string} inFormat - Format of the input coordinates + * @param {string} inDelim - The delimiter splitting the lat/long of the input * @param {string} outFormat - Format to convert to + * @param {string} outDelim - The delimiter to separate the output with + * @param {string} includeDir - Whether or not to include the compass direction in the output * @param {number} precision - Precision of the result - * @returns {string[]} Array containing the converted latitude and longitude + * @returns {string} A formatted string of the converted co-ordinates */ -export function convertCoordinates (inLat, inLong, inFormat, outFormat, precision) { - let convLat = inLat; - let convLong = inLong; +export function convertCoordinates (input, inFormat, inDelim, outFormat, outDelim, includeDir, precision) { + let isPair = false, + split, + latlon, + conv, + inLatDir, + inLongDir; + + if (inDelim === "Auto") { + inDelim = findDelim(input); + } else { + inDelim = realDelim(inDelim); + } + if (inFormat === "Auto") { + inFormat = findFormat(input, inDelim); + if (inFormat === null) { + throw "Unable to detect the input format automatically."; + } + } + if (inDelim === null && !inFormat.includes("Direction")) { + throw "Unable to detect the input delimiter automatically."; + } + outDelim = realDelim(outDelim); + + if (!NO_CHANGE.includes(inFormat)) { + split = input.split(inDelim); + if (split.length > 1) { + isPair = true; + } + } else { + input = input.replace(inDelim, ""); + isPair = true; + } + + if (inFormat.includes("Degrees")) { + [inLatDir, inLongDir] = findDirs(input, inDelim); + } + if (inFormat === "Geohash") { - const hash = geohash.decode(inLat); - convLat = hash.latitude.toString(); - convLong = hash.longitude.toString(); + const hash = geohash.decode(input.replace(/[^A-Za-z0-9]/g, "")); + latlon = new geodesy.LatLonEllipsoidal(hash.latitude, hash.longitude); } else if (inFormat === "Military Grid Reference System") { - const utm = geodesy.Mgrs.parse(inLat).toUtm(); - const result = utm.toLatLonE().toString("d", 4).replace(/[^0-9.,]/g, ""); - const splitResult = result.split(","); - if (splitResult.length === 2) { - convLat = splitResult[0]; - convLong = splitResult[1]; - } + const utm = geodesy.Mgrs.parse(input.replace(/[^A-Za-z0-9]/g, "")).toUtm(); + latlon = utm.toLatLonE(); } else if (inFormat === "Ordnance Survey National Grid") { - const osng = geodesy.OsGridRef.parse(inLat); - const latlon = geodesy.OsGridRef.osGridToLatLon(osng, geodesy.LatLonEllipsoidal.datum.WGS84); - const result = latlon.toString("d", 4).replace(/[^0-9.,]/g, ""); - const splitResult = result.split(","); - if (splitResult.length === 2) { - convLat = splitResult[0]; - convLong = splitResult[1]; + const osng = geodesy.OsGridRef.parse(input.replace(/[^A-Za-z0-9]/g, "")); + latlon = geodesy.OsGridRef.osGridToLatLon(osng); + } else if (inFormat === "Universal Transverse Mercator") { + if (/^[\d]{2}[A-Za-z]/.test(input)) { + input = input.slice(0, 2) + " " + input.slice(2); + } + const utm = geodesy.Utm.parse(input); + latlon = utm.toLatLonE(); + } else if (inFormat === "Degrees Minutes Seconds") { + if (isPair) { + split[0] = split[0].replace(/[NnEeSsWw]/g, "").trim(); + split[1] = split[1].replace(/[NnEeSsWw]/g, "").trim(); + const splitLat = split[0].split(/[°′″'"\s]/g), + splitLong = split[1].split(/[°′″'"\s]/g); + + if (splitLat.length >= 3 && splitLong.length >= 3) { + const lat = convDMSToDD(parseFloat(splitLat[0]), parseFloat(splitLat[1]), parseFloat(splitLat[2]), 10); + const long = convDMSToDD(parseFloat(splitLong[0]), parseFloat(splitLong[1]), parseFloat(splitLong[2]), 10); + latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees); + } + } else { + // Create a new latlon object anyway, but we can ignore the lon value + split[0] = split[0].replace(/[NnEeSsWw]/g, "").trim(); + const splitLat = split[0].split(/[°′″'"\s]/g); + if (splitLat.length >= 3) { + const lat = convDMSToDD(parseFloat(splitLat[0]), parseFloat(splitLat[1]), parseFloat(splitLat[2])); + latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lat.degrees); + } + } + } else if (inFormat === "Degrees Decimal Minutes") { + if (isPair) { + const splitLat = splitInput(split[0]); + const splitLong = splitInput(split[1]); + if (splitLat.length !== 2 || splitLong.length !== 2) { + throw "Invalid co-ordinate format for Degrees Decimal Minutes."; + } + const lat = convDDMToDD(splitLat[0], splitLat[1], 10); + const long = convDDMToDD(splitLong[0], splitLong[1], 10); + latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees); + } else { + const splitLat = splitInput(input); + if (splitLat.length !== 2) { + throw "Invalid co-ordinate format for Degrees Decimal Minutes."; + } + const lat = convDDMToDD(splitLat[0], splitLat[1], 10); + latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lat.degrees); + } + } else if (inFormat === "Decimal Degrees") { + if (isPair) { + const splitLat = splitInput(split[0]); + const splitLong = splitInput(split[1]); + if (splitLat.length !== 1 || splitLong.length !== 1) { + throw "Invalid co-ordinate format for Decimal Degrees."; + } + latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLong[0]); + } else { + const splitLat = splitInput(split[0]); + if (splitLat.length !== 1) { + throw "Invalid co-ordinate format for Decimal Degrees."; + } + latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLat[0]); } } else { - convLat = convertSingleCoordinate(inLat, inFormat, "Decimal Degrees", 15).split("°"); - convLong = convertSingleCoordinate(inLong, inFormat, "Decimal Degrees", 15).split("°"); + throw "Invalid input co-ordinate format selected."; } - // Convert Geohash and MGRS here, as they need both the lat and long values - if (outFormat === "Geohash") { - convLat = geohash.encode(parseFloat(convLat), parseFloat(convLong), precision); + // Everything is now a geodesy latlon object + if (outFormat === "Decimal Degrees") { + conv = latlon.toString("d", precision); + if (!isPair) { + conv = conv.split(",")[0]; + } + } else if (outFormat === "Degrees Decimal Minutes") { + conv = latlon.toString("dm", precision); + if (!isPair) { + conv = conv.split(",")[0]; + } + } else if (outFormat === "Degrees Minutes Seconds") { + conv = latlon.toString("dms", precision); + if (!isPair) { + conv = conv.split(",")[0]; + } + } else if (outFormat === "Geohash") { + conv = geohash.encode(latlon.lat.toString(), latlon.lon.toString(), precision); } else if (outFormat === "Military Grid Reference System") { - const utm = new geodesy.LatLonEllipsoidal(parseFloat(convLat), parseFloat(convLong)).toUtm(); + const utm = latlon.toUtm(); const mgrs = utm.toMgrs(); - convLat = mgrs.toString(); + conv = mgrs.toString(precision); } else if (outFormat === "Ordnance Survey National Grid") { - const latlon = new geodesy.LatLonEllipsoidal(parseFloat(convLat), parseFloat(convLong)); const osng = geodesy.OsGridRef.latLonToOsGrid(latlon); - convLat = osng.toString(); - if (convLat === "") { - throw "Couldn't convert co-ordinates to Ordnance Survey National Grid. Are they out of range?"; + if (osng.toString() === "") { + throw "Could not convert co-ordinates to OS National Grid. Are the co-ordinates in range?"; } - } else { - convLat = convertSingleCoordinate(convLat.toString(), "Decimal Degrees", outFormat, precision); - convLong = convertSingleCoordinate(convLong.toString(), "Decimal Degrees", outFormat, precision); + conv = osng.toString(precision); + } else if (outFormat === "Universal Transverse Mercator") { + const utm = latlon.toUtm(); + conv = utm.toString(precision); } - return [convLat, convLong]; + if (conv === undefined) { + throw "Error converting co-ordinates."; + } + if (outFormat.includes("Degrees")) { + let [latDir, longDir] = findDirs(conv, outDelim); + if (inLatDir !== undefined) { + latDir = inLatDir; + } + if (inLongDir !== undefined) { + longDir = inLongDir; + } + // DMS/DDM/DD + conv = conv.replace(", ", outDelim); + // Remove any directions from the current string, + // so we can put them where we want them + conv = conv.replace(/[NnEeSsWw]/g, ""); + if (includeDir !== "None") { + let outConv = ""; + if (!isPair) { + if (includeDir === "Before") { + outConv += latDir + " " + conv; + } else { + outConv += conv + " " + latDir; + } + } else { + const splitConv = conv.split(outDelim); + if (splitConv.length === 2) { + if (includeDir === "Before") { + outConv += latDir + " "; + } + outConv += splitConv[0]; + if (includeDir === "After") { + outConv += " " + latDir; + } + outConv += outDelim; + if (includeDir === "Before") { + outConv += longDir + " "; + } + outConv += splitConv[1]; + if (includeDir === "After") { + outConv += " " + longDir; + } + } + } + conv = outConv; + } + } + + return conv; } /** - * @param {string} input - The input co-ordinate to be converted - * @param {string} inFormat - The format of the input co-ordinates - * @param {string} outFormat - The format which input should be converted to - * @param {boolean} returnRaw - When true, returns the raw float instead of a String - * @returns {string|{Object}} The converted co-ordinate result, as either the raw object or a formatted string - */ -export function convertSingleCoordinate (input, inFormat, outFormat, precision, returnRaw = false){ - let converted; - precision = Math.pow(10, precision); - const convData = splitInput(input); - // Convert everything to decimal degrees first - switch (inFormat) { - case "Degrees Minutes Seconds": - if (convData.length < 3) { - throw "Invalid co-ordinates format."; - } - converted = convDMSToDD(convData[0], convData[1], convData[2], precision); - break; - case "Degrees Decimal Minutes": - if (convData.length < 2) { - throw "Invalid co-ordinates format."; - } - converted = convDDMToDD(convData[0], convData[1], precision); - break; - case "Decimal Degrees": - if (convData.length < 1) { - throw "Invalid co-ordinates format."; - } - converted = convDDToDD(convData[0], precision); - break; - default: - throw "Unknown input format selection."; - } - - // Convert from decimal degrees to the output format - switch (outFormat) { - case "Decimal Degrees": - break; - case "Degrees Minutes Seconds": - converted = convDDToDMS(converted.degrees); - break; - case "Degrees Decimal Minutes": - converted = convDDToDDM(converted.degrees, precision); - break; - default: - throw "Unknown output format selection."; - } - if (returnRaw) { - return converted; - } else { - return converted.string; - } -} - -/** - * Split up the input using a space, and sanitise the result + * Split up the input using a space or degrees signs, and sanitise the result * @param {string} input - The input data to be split * @returns {number[]} An array of the different items in the string, stored as floats */ function splitInput (input){ const split = []; - input.split(" ").forEach(item => { + input.split(/[°′″'"\s]/).forEach(item => { // Remove any character that isn't a digit item = item.replace(/[^0-9.-]/g, ""); if (item.length > 0){ - split.push(parseFloat(item, 10)); + split.push(parseFloat(item)); } }); return split; @@ -245,47 +337,153 @@ function convDDToDDM (decDegrees, precision) { } /** - * + * Finds and returns the compass directions in an input string + * @param {string} input - The input co-ordinates containing the direction + * @param {string} delim - The delimiter separating latitide and longitude + * @returns {string[]} String array containing the latitude and longitude directions + */ +export function findDirs(input, delim) { + const upperInput = input.toUpperCase(); + const dirExp = new RegExp(/[NESW]/g); + + const dirs = upperInput.match(dirExp); + + if (dirExp.test(upperInput)) { + // If there's actually compass directions in the string + if (dirs.length <= 2 && dirs.length >= 1) { + if (dirs.length === 2) { + return [dirs[0], dirs[1]]; + } else { + return [dirs[0], ""]; + } + } + } + // Nothing was returned, so guess the directions + let lat = upperInput, + long, + latDir = "", + longDir = ""; + if (!delim.includes("Direction")) { + if (upperInput.includes(delim)) { + const split = upperInput.split(delim); + if (split.length > 1) { + if (split[0] === "") { + lat = split[1]; + } else { + lat = split[0]; + } + if (split.length > 2) { + if (split[2] !== "") { + long = split[2]; + } + } + } + } + } else { + const split = upperInput.split(dirExp); + if (split.length > 1) { + if (split[0] === "") { + lat = split[1]; + } else { + lat = split[0]; + } + if (split.length > 2) { + if (split[2] !== "") { + long = split[2]; + } + } + } + } + if (lat) { + lat = parseFloat(lat); + if (lat < 0) { + latDir = "S"; + } else { + latDir = "N"; + } + } + if (long) { + long = parseFloat(long); + if (long < 0) { + longDir = "W"; + } else { + longDir = "E"; + } + } + + return [latDir, longDir]; +} + +/** + * Detects the co-ordinate format of the input data * @param {string} input - The input data whose format we need to detect * @param {string} delim - The delimiter separating the data in input * @returns {string} The input format */ export function findFormat (input, delim) { - input = input.trim(); let testData; - if (delim.includes("Direction")) { + const mgrsPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]{1}\s?[A-HJ-NP-Z][A-HJ-NP-V]\s?[0-9\s]+/), + osngPattern = new RegExp(/^[STNHO][A-HJ-Z][0-9]+$/), + geohashPattern = new RegExp(/^[0123456789BCDEFGHJKMNPQRSTUVWXYZ]+$/), + utmPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]\s[0-9\.]+\s?[0-9\.]+$/), + degPattern = new RegExp(/[°'"]/g); + input = input.trim(); + if (delim !== null && delim.includes("Direction")) { const split = input.split(/[NnEeSsWw]/); - if (split.length > 0) { + if (split.length > 1) { if (split[0] === "") { - // Direction Preceding testData = split[1]; } else { - // Direction Following testData = split[0]; } } - } else if (delim !== "") { - const split = input.split(delim); - if (!input.includes(delim)) { + } else if (delim !== null && delim !== "") { + if (input.includes(delim)) { + const split = input.split(delim); + if (split.length > 1) { + if (split[0] === "") { + testData = split[1]; + } else { + testData = split[0]; + } + } + } else { testData = input; } - if (split.length > 0) { - if (split[0] !== "") { - testData = split[0]; - } else if (split.length > 1) { - testData = split[1]; - } - } } // Test MGRS and Geohash - if (input.split(" ").length <= 1) { - const filteredInput = input.replace(/[^A-Za-z0-9]/, "").toUpperCase(); - const mgrsPattern = new RegExp(/^[0-9]{2}[C-HJ-NP-X]{2}[A-Z]+/); - const geohashPattern = new RegExp(/^[0123456789BCDEFGHJKMNPQRSTUVWXYZ]+$/); - if (mgrsPattern.test(filteredInput)) { + if (!degPattern.test(input)) { + const filteredInput = input.toUpperCase(); + const isMgrs = mgrsPattern.test(filteredInput); + const isOsng = osngPattern.test(filteredInput); + const isGeohash = geohashPattern.test(filteredInput); + const isUtm = utmPattern.test(filteredInput); + if (isMgrs && (isOsng || isGeohash)) { + if (filteredInput.includes("I")) { + // Only MGRS can have an i! + return "Military Grid Reference System"; + } + } + if (isUtm) { + return "Universal Transverse Mercator"; + } + if (isOsng && isGeohash) { + // Geohash doesn't have A, L or O, but OSNG does. + const testExp = new RegExp(/[ALO]/g); + if (testExp.test(filteredInput)) { + return "Ordnance Survey National Grid"; + } else { + return "Geohash"; + } + } + if (isMgrs) { return "Military Grid Reference System"; - } else if (geohashPattern.test(filteredInput)) { + } + if (isOsng) { + return "Ordnance Survey National Grid"; + } + if (isGeohash) { return "Geohash"; } } @@ -312,7 +510,7 @@ export function findFormat (input, delim) { */ export function findDelim (input) { input = input.trim(); - const delims = [",", ";", ":"]; + const delims = [",", ";", ":", " "]; const testDir = input.match(/[NnEeSsWw]/g); if (testDir !== null && testDir.length > 0 && testDir.length < 3) { // Possibly contains a direction @@ -340,3 +538,19 @@ export function findDelim (input) { } return null; } + +/** + * Gets the real string for a delimiter name. + * @param {string} delim The delimiter to be matched + * @returns {string} + */ +export function realDelim (delim) { + return { + "Auto": "Auto", + "Space": " ", + "\\n": "\n", + "Comma": ",", + "Semi-colon": ";", + "Colon": ":" + }[delim]; +} diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index afc95982..770920f4 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -5,9 +5,7 @@ */ import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import {FORMATS, STRING_FORMATS, convertCoordinates, convertSingleCoordinate, findDelim, findFormat} from "../lib/ConvertCoordinates"; -import Utils from "../Utils"; +import {FORMATS, convertCoordinates} from "../lib/ConvertCoordinates"; /** * Convert co-ordinate format operation @@ -22,7 +20,7 @@ class ConvertCoordinateFormat extends Operation { this.name = "Convert co-ordinate format"; this.module = "Hashing"; - this.description = "Convert geographical coordinates between different formats.

Supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
  • Ordnance Survey National Grid (OSNG)
"; + this.description = "Convert geographical coordinates between different formats.

Supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
  • Ordnance Survey National Grid (OSNG)
  • Universal Transverse Mercator (UTM)

The operation can try to detect the input co-ordinate format and delimiter automatically, but this may not always work correctly."; this.infoURL = "https://wikipedia.org/wiki/Geographic_coordinate_conversion"; this.inputType = "string"; this.outputType = "string"; @@ -39,6 +37,7 @@ class ConvertCoordinateFormat extends Operation { "Auto", "Direction Preceding", "Direction Following", + "Space", "\\n", "Comma", "Semi-colon", @@ -55,14 +54,21 @@ class ConvertCoordinateFormat extends Operation { "type": "option", "value": [ "Space", - "Direction Preceding", - "Direction Following", "\\n", "Comma", "Semi-colon", "Colon" ] }, + { + "name": "Include Compass Directions", + "type": "option", + "value": [ + "None", + "Before", + "After" + ] + }, { "name": "Precision", "type": "number", @@ -77,148 +83,8 @@ class ConvertCoordinateFormat extends Operation { * @returns {string} */ run(input, args) { - const outFormat = args[2], - outDelim = args[3], - precision = args[4]; - let inFormat = args[0], - inDelim = args[1], - inLat, - inLong, - outLat, - outLong, - latDir = "", - longDir = "", - outSeparator = " "; - - // Autodetect input delimiter - if (inDelim === "Auto") { - inDelim = findDelim(input); - if (inDelim === null) { - inDelim = ""; - } - } else if (!inDelim.includes("Direction")) { - // Get the actual delimiter from the regex - inDelim = String(Utils.regexRep(inDelim)).slice(1, 2); - } - if (inFormat === "Auto") { - inFormat = findFormat(input, inDelim); - if (inFormat === null) { - throw new OperationError("Could not automatically detect the input format."); - } - } - - if (inDelim === "" && (!STRING_FORMATS.includes(inFormat))) { - throw new OperationError("Could not automatically detect the input delimiter."); - } - - // Prepare input data - if (STRING_FORMATS.includes(inFormat)) { - // Geohash only has one value, so just use the input - // Replace anything that isn't a valid character in Geohash / MGRS / OSNG - inLat = input.replace(/[^A-Za-z0-9]/, ""); - } else if (inDelim === "Direction Preceding") { - // Split on the compass directions - const splitInput = input.split(/[NnEeSsWw]/); - const dir = input.match(/[NnEeSsWw]/g); - if (splitInput.length > 1) { - inLat = splitInput[1]; - if (dir !== null) { - latDir = dir[0]; - } - if (splitInput.length > 2) { - inLong = splitInput[2]; - if (dir !== null && dir.length > 1) { - longDir = dir[1]; - } - } - } - } else if (inDelim === "Direction Following") { - // Split on the compass directions - const splitInput = input.split(/[NnEeSsWw]/); - if (splitInput.length >= 1) { - inLat = splitInput[0]; - if (splitInput.length >= 2) { - inLong = splitInput[1]; - } - } - } else { - // Split on the delimiter - const splitInput = input.split(inDelim); - if (splitInput.length > 0) { - inLat = splitInput[0]; - if (splitInput.length >= 2) { - inLong = splitInput[1]; - } - } - } - - if (!STRING_FORMATS.includes(inFormat) && outDelim.includes("Direction")) { - // Match on compass directions, and store the first 2 matches for the output - const dir = input.match(/[NnEeSsWw]/g); - if (dir !== null) { - latDir = dir[0]; - if (dir.length > 1) { - longDir = dir[1]; - } - } - } else if (outDelim === "\\n") { - outSeparator = "\n"; - } else if (outDelim === "Space") { - outSeparator = " "; - } else if (!outDelim.includes("Direction")) { - // Cut out the regex syntax (/) from the delimiter - outSeparator = String(Utils.regexRep(outDelim)).slice(1, 2); - } - - // Convert the co-ordinates - if (inLat !== undefined) { - if (inLong === undefined) { - if (!STRING_FORMATS.includes(inFormat)) { - if (STRING_FORMATS.includes(outFormat)){ - throw new OperationError(`${outFormat} needs both a latitude and a longitude to be calculated`); - } - } - if (STRING_FORMATS.includes(inFormat)) { - // Geohash conversion is in convertCoordinates despite needing - // only one input as it needs to output two values - inLat = inLat.replace(/[^A-Za-z0-9]/g, ""); - [outLat, outLong] = convertCoordinates(inLat, inLat, inFormat, outFormat, precision); - } else { - outLat = convertSingleCoordinate(inLat, inFormat, outFormat, precision); - } - } else { - [outLat, outLong] = convertCoordinates(inLat, inLong, inFormat, outFormat, precision); - } - } else { - throw new OperationError("No co-ordinates were detected in the input."); - } - - // Output conversion results if successful - if (outLat !== undefined) { - let output = ""; - if (outDelim === "Direction Preceding" && !STRING_FORMATS.includes(outFormat)) { - output += latDir += " "; - } - output += outLat; - if (outDelim === "Direction Following" && !STRING_FORMATS.includes(outFormat)) { - output += " " + latDir; - } - output += outSeparator; - - if (outLong !== undefined && !STRING_FORMATS.includes(outFormat)) { - if (outDelim === "Direction Preceding") { - output += longDir + " "; - } - output += outLong; - if (outDelim === "Direction Following") { - output += " " + longDir; - } - output += outSeparator; - } - return output; - } else { - throw new OperationError("Co-ordinate conversion failed."); - } + const [inFormat, inDelim, outFormat, outDelim, incDirection, precision] = args; + return convertCoordinates(input, inFormat, inDelim, outFormat, outDelim, incDirection, precision); } } From 0602f457cef97046f03ef8dc81156de2a18ed613 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 15 Jan 2019 16:24:29 +0000 Subject: [PATCH 101/147] Added initial tests & counts support --- src/core/operations/YARARules.mjs | 13 ++++++++++--- tests/operations/index.mjs | 1 + tests/operations/tests/YARA.mjs | 24 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 tests/operations/tests/YARA.mjs diff --git a/src/core/operations/YARARules.mjs b/src/core/operations/YARARules.mjs index 58b3576c..c453d6b8 100644 --- a/src/core/operations/YARARules.mjs +++ b/src/core/operations/YARARules.mjs @@ -49,6 +49,12 @@ class YARARules extends Operation { type: "boolean", hint: "Show the metadata of each rule", value: false + }, + { + name: "Show counts", + type: "boolean", + hint: "Show the number of matches per rule", + value: true } ]; } @@ -61,7 +67,7 @@ class YARARules extends Operation { run(input, args) { if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Instantiating YARA."); - const [rules, showStrings, showLengths, showMeta] = args; + const [rules, showStrings, showLengths, showMeta, showCounts] = args; return new Promise((resolve, reject) => { Yara().then(yara => { if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Converting data for YARA."); @@ -97,10 +103,11 @@ class YARARules extends Operation { } meta = meta.slice(0, -2) + "]"; } + const countString = showCounts ? `${matches.size()} time${matches.size() > 1 ? "s" : ""}` : ""; if (matches.size() === 0 || !(showStrings || showLengths)) { - matchString += `Input matches rule "${rule.ruleName}"${meta}.\n`; + matchString += `Input matches rule "${rule.ruleName}"${meta}${countString.length > 0 ? ` ${countString}`: ""}.\n`; } else { - matchString += `Rule "${rule.ruleName}"${meta} matches:\n`; + matchString += `Rule "${rule.ruleName}"${meta} matches (${countString}):\n`; for (let j = 0; j < matches.size(); j++) { const match = matches.get(j); if (showStrings || showLengths) { diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index da9d41be..48bd08a8 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -82,6 +82,7 @@ import "./tests/TranslateDateTimeFormat"; import "./tests/Magic"; import "./tests/ParseTLV"; import "./tests/Media"; +import "./tests/YARA.mjs"; // Cannot test operations that use the File type yet //import "./tests/SplitColourChannels"; diff --git a/tests/operations/tests/YARA.mjs b/tests/operations/tests/YARA.mjs new file mode 100644 index 00000000..e3c28ef1 --- /dev/null +++ b/tests/operations/tests/YARA.mjs @@ -0,0 +1,24 @@ +/** + * YARA Rules tests. + * + * @author Matt C [matt@artemisbot.uk] + * + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ +import TestRegister from "../TestRegister"; + +TestRegister.addTests([ + { + name: "YARA Match: simple foobar", + input: "foobar foobar bar foo foobar", + expectedOutput: "Rule \"foo\" matches (4 times):\nPos 0, length 3, identifier $re1, data: \"foo\"\nPos 7, length 3, identifier $re1, data: \"foo\"\nPos 18, length 3, identifier $re1, data: \"foo\"\nPos 22, length 3, identifier $re1, data: \"foo\"\nRule \"bar\" matches (4 times):\nPos 3, length 3, identifier $re1, data: \"bar\"\nPos 10, length 3, identifier $re1, data: \"bar\"\nPos 14, length 3, identifier $re1, data: \"bar\"\nPos 25, length 3, identifier $re1, data: \"bar\"\n", + recipeConfig: [ + { + "op": "YARA Rules", + "args": ["rule foo {strings: $re1 = /foo/ condition: $re1} rule bar {strings: $re1 = /bar/ condition: $re1}", true, true, true, true], + } + ], + }, +]); + From fcc39a03973276b96aaac22b6c8c39c217f05664 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 15 Jan 2019 23:42:05 +0000 Subject: [PATCH 102/147] Added File upload support to textarea --- src/web/Manager.mjs | 3 ++ src/web/RecipeWaiter.mjs | 63 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index d33616a4..3f4af771 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -137,6 +137,9 @@ class Manager { this.addDynamicListener("#rec-list li.operation > div", "dblclick", this.recipe.operationChildDblclick, this.recipe); this.addDynamicListener("#rec-list .dropdown-menu.toggle-dropdown a", "click", this.recipe.dropdownToggleClick, this.recipe); this.addDynamicListener("#rec-list", "operationremove", this.recipe.opRemove.bind(this.recipe)); + this.addDynamicListener("textarea.arg", "dragover", this.recipe.textArgDragover, this.recipe); + this.addDynamicListener("textarea.arg", "dragleave", this.recipe.textArgDragLeave, this.recipe); + this.addDynamicListener("textarea.arg", "drop", this.recipe.textArgDrop, this.recipe); // Input this.addMultiEventListener("#input-text", "keyup", this.input.inputChange, this.input); diff --git a/src/web/RecipeWaiter.mjs b/src/web/RecipeWaiter.mjs index b913fede..a7a9e2e3 100755 --- a/src/web/RecipeWaiter.mjs +++ b/src/web/RecipeWaiter.mjs @@ -453,6 +453,69 @@ class RecipeWaiter { window.dispatchEvent(this.manager.statechange); } + /** + * Handler for text argument dragover events. + * Gives the user a visual cue to show that items can be dropped here. + * + * @param {event} e + */ + textArgDragover (e) { + // This will be set if we're dragging an operation + if (e.dataTransfer.effectAllowed === "move") + return false; + + e.stopPropagation(); + e.preventDefault(); + e.target.closest("textarea.arg").classList.add("dropping-file"); + } + + /** + * Handler for text argument dragleave events. + * Removes the visual cue. + * + * @param {event} e + */ + textArgDragLeave (e) { + e.stopPropagation(); + e.preventDefault(); + e.target.classList.remove("dropping-file"); + } + + /** + * Handler for text argument drop events. + * Loads the dragged data into the argument textarea. + * + * @param {event} e + */ + textArgDrop(e) { + // This will be set if we're dragging an operation + if (e.dataTransfer.effectAllowed === "move") + return false; + + e.stopPropagation(); + e.preventDefault(); + const targ = e.target; + const file = e.dataTransfer.files[0]; + const text = e.dataTransfer.getData("Text"); + + targ.classList.remove("dropping-file"); + + if (text) { + targ.value = text; + return; + } + + if (file) { + const reader = new FileReader(); + const self = this; + reader.onload = function (e) { + targ.value = e.target.result; + self.ingChange(); + }; + reader.readAsText(file); + } + } + /** * Sets register values. From 3dfaaf4c2501b236ec5fddfb38df670a188e81f3 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 15 Jan 2019 23:45:40 +0000 Subject: [PATCH 103/147] Update libyara for test pass --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6ba03db3..c7706dcb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7756,9 +7756,9 @@ "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, "libyara-wasm": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.9.tgz", - "integrity": "sha512-GTR8GD7q2xCwRzQpoSVh31KPbH145tCKaU3DAnQMpkf4aKqjnVpQlq6gUjyDhvw8RPZ2tamyQad5hg42KsVvRw==" + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.10.tgz", + "integrity": "sha512-Hn3u8wb2s6YyH8LYAVB59S05aj4vRDyLe6R3I7LvW0L2Atsc3WfaAfR1i0PgdNXYznE36FCm22pK+xLJ6CJozA==" }, "livereload-js": { "version": "2.4.0", diff --git a/package.json b/package.json index a18331d8..33c8dccd 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", - "libyara-wasm": "0.0.9", + "libyara-wasm": "0.0.10", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", From d1961ca3fa3b6ff37d9333d6e07e0ab580d89b68 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 16 Jan 2019 01:15:51 +0000 Subject: [PATCH 104/147] Marginally reduced size of libyara-wasm --- package-lock.json | 60 ++++++++++++++++++++++------------------------- package.json | 2 +- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index 08086ee7..932da37f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1497,7 +1497,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1615,7 +1615,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -1700,7 +1700,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1848,7 +1848,7 @@ }, "axios": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "dev": true, "requires": { @@ -2286,7 +2286,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -2323,7 +2323,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -2388,7 +2388,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -2551,7 +2551,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -2600,7 +2600,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -3133,7 +3133,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -3146,7 +3146,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -4794,7 +4794,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -5098,14 +5098,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5125,8 +5123,7 @@ "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", @@ -5274,7 +5271,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5985,7 +5981,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -6129,7 +6125,7 @@ "dependencies": { "colors": { "version": "1.1.2", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true } @@ -6193,7 +6189,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -7760,9 +7756,9 @@ "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, "libyara-wasm": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.10.tgz", - "integrity": "sha512-Hn3u8wb2s6YyH8LYAVB59S05aj4vRDyLe6R3I7LvW0L2Atsc3WfaAfR1i0PgdNXYznE36FCm22pK+xLJ6CJozA==" + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.11.tgz", + "integrity": "sha512-rglapPFo0IHPNksWYQXI8oqftXYj5mOGOf4BXtbSySVRX71pro4BehNjJ5qEpjYx+roGvNkcAD9zCsitA08sxw==" }, "livereload-js": { "version": "2.4.0", @@ -8480,7 +8476,7 @@ "dependencies": { "commander": { "version": "2.15.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true, "optional": true @@ -8931,7 +8927,7 @@ "dependencies": { "colors": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" }, "underscore": { @@ -10161,13 +10157,13 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true }, "winston": { "version": "2.1.1", - "resolved": "http://registry.npmjs.org/winston/-/winston-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", "dev": true, "requires": { @@ -10182,7 +10178,7 @@ "dependencies": { "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true }, @@ -12947,7 +12943,7 @@ "dependencies": { "async": { "version": "0.9.2", - "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -13675,14 +13671,14 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true, "optional": true }, "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true, "optional": true diff --git a/package.json b/package.json index 4dd5b07b..dd71321f 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", - "libyara-wasm": "0.0.10", + "libyara-wasm": "0.0.11", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", From 69797e58cb6fc5d5622c55dbd42fab83de1ad668 Mon Sep 17 00:00:00 2001 From: j433866 Date: Wed, 16 Jan 2019 16:57:58 +0000 Subject: [PATCH 105/147] Add better error handling. Also now doesn't do anything if there's no input --- src/core/operations/ConvertCoordinateFormat.mjs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index 770920f4..09e1620c 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -5,6 +5,7 @@ */ import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; import {FORMATS, convertCoordinates} from "../lib/ConvertCoordinates"; /** @@ -37,7 +38,6 @@ class ConvertCoordinateFormat extends Operation { "Auto", "Direction Preceding", "Direction Following", - "Space", "\\n", "Comma", "Semi-colon", @@ -83,8 +83,17 @@ class ConvertCoordinateFormat extends Operation { * @returns {string} */ run(input, args) { - const [inFormat, inDelim, outFormat, outDelim, incDirection, precision] = args; - return convertCoordinates(input, inFormat, inDelim, outFormat, outDelim, incDirection, precision); + if (input.replace(/[\s+]/g, "") !== "") { + const [inFormat, inDelim, outFormat, outDelim, incDirection, precision] = args; + try { + const result = convertCoordinates(input, inFormat, inDelim, outFormat, outDelim, incDirection, precision); + return result; + } catch (error) { + throw new OperationError(error); + } + } else { + return input; + } } } From 439654ed7f11d7c7bf3a4364d0f4bb95dce34aee Mon Sep 17 00:00:00 2001 From: j433866 Date: Thu, 17 Jan 2019 13:49:36 +0000 Subject: [PATCH 106/147] Add tests for new co-ordinate conversion module. Removed To/From geohash tests --- test/index.mjs | 3 +- .../operations/ConvertCoordinateFormat.mjs | 211 ++++++++++++++++++ test/tests/operations/FromGeohash.mjs | 55 ----- test/tests/operations/ToGeohash.mjs | 55 ----- 4 files changed, 212 insertions(+), 112 deletions(-) create mode 100644 test/tests/operations/ConvertCoordinateFormat.mjs delete mode 100644 test/tests/operations/FromGeohash.mjs delete mode 100644 test/tests/operations/ToGeohash.mjs diff --git a/test/index.mjs b/test/index.mjs index 9c11f6ae..8d7bd798 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -44,7 +44,6 @@ import "./tests/operations/DateTime"; import "./tests/operations/ExtractEmailAddresses"; import "./tests/operations/Fork"; import "./tests/operations/FromDecimal"; -import "./tests/operations/FromGeohash"; import "./tests/operations/Hash"; import "./tests/operations/HaversineDistance"; import "./tests/operations/Hexdump"; @@ -74,10 +73,10 @@ import "./tests/operations/SetIntersection"; import "./tests/operations/SetUnion"; import "./tests/operations/StrUtils"; import "./tests/operations/SymmetricDifference"; -import "./tests/operations/ToGeohash.mjs"; import "./tests/operations/TranslateDateTimeFormat"; import "./tests/operations/Magic"; import "./tests/operations/ParseTLV"; +import "./tests/operations/ConvertCoordinateFormat"; let allTestsPassing = true; const testStatusCounts = { diff --git a/test/tests/operations/ConvertCoordinateFormat.mjs b/test/tests/operations/ConvertCoordinateFormat.mjs new file mode 100644 index 00000000..89690b97 --- /dev/null +++ b/test/tests/operations/ConvertCoordinateFormat.mjs @@ -0,0 +1,211 @@ +/** + * Convert co-ordinate format tests + * + * @author j433866 + * + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +/** + * TEST CO-ORDINATES + * DD: 51.504°,-0.126°, + * DDM: 51° 30.24',-0° 7.56', + * DMS: 51° 30' 14.4",-0° 7' 33.6", + * Geohash: gcpvj0h0x, + * MGRS: 30U XC 99455 09790, + * OSNG: TQ 30163 80005, + * UTM: 30N 699456 5709791, + */ + +import TestRegister from "../../TestRegister"; + +TestRegister.addTests([ + { + name: "Co-ordinates: From Decimal Degrees to Degrees Minutes Seconds", + input: "51.504°,-0.126°,", + expectedOutput: "51° 30' 14.4\",-0° 7' 33.6\",", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Degrees Minutes Seconds", "Comma", "None", 1] + }, + ], + }, + { + name: "Co-ordinates: From Degrees Minutes Seconds to Decimal Degrees", + input: "51° 30' 14.4\",-0° 7' 33.6\",", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Degrees Minutes Seconds", "Comma", "Decimal Degrees", "Comma", "None", 3] + }, + ], + }, + { + name: "Co-ordinates: From Decimal Degrees to Degrees Decimal Minutes", + input: "51.504°,-0.126°,", + expectedOutput: "51° 30.24',-0° 7.56',", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Degrees Decimal Minutes", "Comma", "None", 2] + } + ] + }, + { + name: "Co-ordinates: From Degrees Decimal Minutes to Decimal Degrees", + input: "51° 30.24',-0° 7.56',", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Degrees Decimal Minutes", "Comma", "Decimal Degrees", "Comma", "None", 3] + } + ] + }, + { + name: "Co-ordinates: From Decimal Degrees to Decimal Degrees", + input: "51.504°,-0.126°,", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Decimal Degrees", "Comma", "None", 3] + } + ] + }, + { + name: "Co-ordinates: From Decimal Degrees to Geohash", + input: "51.504°,-0.126°,", + expectedOutput: "gcpvj0h0x,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Geohash", "Comma", "None", 9] + }, + ], + }, + { + name: "Co-ordinates: From Geohash to Decimal Degrees", + input: "gcpvj0h0x,", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Geohash", "Comma", "Decimal Degrees", "Comma", "None", 3] + }, + ], + }, + { + name: "Co-ordinates: From Decimal Degrees to MGRS", + input: "51.504°,-0.126°,", + expectedOutput: "30U XC 99455 09790,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Military Grid Reference System", "Comma", "None", 10] + }, + ], + }, + { + name: "Co-ordinates: From MGRS to Decimal Degrees", + input: "30U XC 99455 09790,", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Military Grid Reference System", "Comma", "Decimal Degrees", "Comma", "None", 3] + } + ] + }, + { + name: "Co-ordinates: From Decimal Degrees to OSNG", + input: "51.504°,-0.126°,", + expectedOutput: "TQ 30163 80005,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Ordnance Survey National Grid", "Comma", "None", 10] + }, + ], + }, + { + name: "Co-ordinates: From OSNG to Decimal Degrees", + input: "TQ 30163 80005,", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Ordnance Survey National Grid", "Comma", "Decimal Degrees", "Comma", "None", 3] + }, + ], + }, + { + name: "Co-ordinates: From Decimal Degrees to UTM", + input: "51.504°,-0.126°,", + expectedOutput: "30 N 699456 5709791,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Universal Transverse Mercator", "Comma", "None", 0] + }, + ], + }, + { + name: "Co-ordinates: From UTM to Decimal Degrees", + input: "30 N 699456 5709791,", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Universal Transverse Mercator", "Comma", "Decimal Degrees", "Comma", "None", 3] + }, + ], + }, + { + name: "Co-ordinates: Directions in input, not output", + input: "N51.504°,W0.126°,", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Decimal Degrees", "Comma", "None", 3] + }, + ], + }, + { + name: "Co-ordinates: Directions in input and output", + input: "N51.504°,W0.126°,", + expectedOutput: "N 51.504°,W 0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Decimal Degrees", "Comma", "Before", 3] + }, + ], + }, + { + name: "Co-ordinates: Directions not in input, in output", + input: "51.504°,-0.126°,", + expectedOutput: "N 51.504°,W 0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Decimal Degrees", "Comma", "Before", 3] + }, + ], + }, + { + name: "Co-ordinates: Directions not in input, in converted output", + input: "51.504°,-0.126°,", + expectedOutput: "N 51° 30' 14.4\",W 0° 7' 33.6\",", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Degrees Minutes Seconds", "Comma", "Before", 3] + }, + ], + } +]); diff --git a/test/tests/operations/FromGeohash.mjs b/test/tests/operations/FromGeohash.mjs deleted file mode 100644 index 2ac68c58..00000000 --- a/test/tests/operations/FromGeohash.mjs +++ /dev/null @@ -1,55 +0,0 @@ -/** - * To Geohash tests - * - * @author gchq77703 - * @copyright Crown Copyright 2018 - * @license Apache-2.0 - */ -import TestRegister from "../../TestRegister"; - -TestRegister.addTests([ - { - name: "From Geohash", - input: "ww8p1r4t8", - expectedOutput: "37.83238649368286,112.55838632583618", - recipeConfig: [ - { - op: "From Geohash", - args: [], - }, - ], - }, - { - name: "From Geohash", - input: "ww8p1r", - expectedOutput: "37.83416748046875,112.5604248046875", - recipeConfig: [ - { - op: "From Geohash", - args: [], - }, - ], - }, - { - name: "From Geohash", - input: "ww8", - expectedOutput: "37.265625,113.203125", - recipeConfig: [ - { - op: "From Geohash", - args: [], - }, - ], - }, - { - name: "From Geohash", - input: "w", - expectedOutput: "22.5,112.5", - recipeConfig: [ - { - op: "From Geohash", - args: [], - }, - ], - }, -]); diff --git a/test/tests/operations/ToGeohash.mjs b/test/tests/operations/ToGeohash.mjs deleted file mode 100644 index b50e7280..00000000 --- a/test/tests/operations/ToGeohash.mjs +++ /dev/null @@ -1,55 +0,0 @@ -/** - * To Geohash tests - * - * @author gchq77703 - * @copyright Crown Copyright 2018 - * @license Apache-2.0 - */ -import TestRegister from "../../TestRegister"; - -TestRegister.addTests([ - { - name: "To Geohash", - input: "37.8324,112.5584", - expectedOutput: "ww8p1r4t8", - recipeConfig: [ - { - op: "To Geohash", - args: [9], - }, - ], - }, - { - name: "To Geohash", - input: "37.9324,-112.2584", - expectedOutput: "9w8pv3ruj", - recipeConfig: [ - { - op: "To Geohash", - args: [9], - }, - ], - }, - { - name: "To Geohash", - input: "37.8324,112.5584", - expectedOutput: "ww8", - recipeConfig: [ - { - op: "To Geohash", - args: [3], - }, - ], - }, - { - name: "To Geohash", - input: "37.9324,-112.2584", - expectedOutput: "9w8pv3rujxy5b99", - recipeConfig: [ - { - op: "To Geohash", - args: [15], - }, - ], - }, -]); From 4bd923dc063f901d54237204834ad0b85295964b Mon Sep 17 00:00:00 2001 From: j433866 Date: Thu, 17 Jan 2019 13:53:42 +0000 Subject: [PATCH 107/147] Improved handling of negative numbers and weirder inputs. Negative numbers shouldn't make it go weird any more. Automatic detection of input formats should be more reliable. --- src/core/lib/ConvertCoordinates.mjs | 352 +++++++++++++++++----------- 1 file changed, 212 insertions(+), 140 deletions(-) diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index cec2439c..d4641fe6 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -23,8 +23,7 @@ export const FORMATS = [ ]; /** - * Formats that should be passed to Geodesy module as-is - * Spaces are still removed + * Formats that should be passed to the conversion module as-is */ const NO_CHANGE = [ "Geohash", @@ -48,40 +47,50 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli let isPair = false, split, latlon, - conv, - inLatDir, - inLongDir; + convLat, + convLon, + conv; + // Can't have a precision less than 0! + if (precision < 0) { + precision = 0; + } if (inDelim === "Auto") { + // Try to detect a delimiter in the input. inDelim = findDelim(input); + if (inDelim === null) { + throw "Unable to detect the input delimiter automatically."; + } } else { + // Convert the delimiter argument value to the actual character inDelim = realDelim(inDelim); } if (inFormat === "Auto") { + // Try to detect the format of the input data inFormat = findFormat(input, inDelim); if (inFormat === null) { throw "Unable to detect the input format automatically."; } } - if (inDelim === null && !inFormat.includes("Direction")) { - throw "Unable to detect the input delimiter automatically."; - } + // Convert the output delimiter argument to the real character outDelim = realDelim(outDelim); if (!NO_CHANGE.includes(inFormat)) { split = input.split(inDelim); + // Replace any co-ordinate symbols with spaces so we can split on them later + for (let i = 0; i < split.length; i++) { + split[i] = split[i].replace(/[°˝´'"]/g, " "); + } if (split.length > 1) { isPair = true; } } else { + // Remove any delimiters from the input input = input.replace(inDelim, ""); isPair = true; } - if (inFormat.includes("Degrees")) { - [inLatDir, inLongDir] = findDirs(input, inDelim); - } - + // Conversions from the input format into a geodesy latlon object if (inFormat === "Geohash") { const hash = geohash.decode(input.replace(/[^A-Za-z0-9]/g, "")); latlon = new geodesy.LatLonEllipsoidal(hash.latitude, hash.longitude); @@ -92,6 +101,7 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli const osng = geodesy.OsGridRef.parse(input.replace(/[^A-Za-z0-9]/g, "")); latlon = geodesy.OsGridRef.osGridToLatLon(osng); } else if (inFormat === "Universal Transverse Mercator") { + // Geodesy needs a space between the first 2 digits and the next letter if (/^[\d]{2}[A-Za-z]/.test(input)) { input = input.slice(0, 2) + " " + input.slice(2); } @@ -99,23 +109,25 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli latlon = utm.toLatLonE(); } else if (inFormat === "Degrees Minutes Seconds") { if (isPair) { - split[0] = split[0].replace(/[NnEeSsWw]/g, "").trim(); - split[1] = split[1].replace(/[NnEeSsWw]/g, "").trim(); - const splitLat = split[0].split(/[°′″'"\s]/g), - splitLong = split[1].split(/[°′″'"\s]/g); + // Split up the lat/long into degrees / minutes / seconds values + const splitLat = splitInput(split[0]), + splitLong = splitInput(split[1]); if (splitLat.length >= 3 && splitLong.length >= 3) { - const lat = convDMSToDD(parseFloat(splitLat[0]), parseFloat(splitLat[1]), parseFloat(splitLat[2]), 10); - const long = convDMSToDD(parseFloat(splitLong[0]), parseFloat(splitLong[1]), parseFloat(splitLong[2]), 10); + const lat = convDMSToDD(splitLat[0], splitLat[1], splitLat[2], 10); + const long = convDMSToDD(splitLong[0], splitLong[1], splitLong[2], 10); latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees); + } else { + throw "Invalid co-ordinate format for Degrees Minutes Seconds"; } } else { - // Create a new latlon object anyway, but we can ignore the lon value - split[0] = split[0].replace(/[NnEeSsWw]/g, "").trim(); - const splitLat = split[0].split(/[°′″'"\s]/g); + // Not a pair, so only try to convert one set of co-ordinates + const splitLat = splitInput(split[0]); if (splitLat.length >= 3) { - const lat = convDMSToDD(parseFloat(splitLat[0]), parseFloat(splitLat[1]), parseFloat(splitLat[2])); + const lat = convDMSToDD(splitLat[0], splitLat[1], splitLat[2]); latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lat.degrees); + } else { + throw "Invalid co-ordinate format for Degrees Minutes Seconds"; } } } else if (inFormat === "Degrees Decimal Minutes") { @@ -125,10 +137,12 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli if (splitLat.length !== 2 || splitLong.length !== 2) { throw "Invalid co-ordinate format for Degrees Decimal Minutes."; } + // Convert to decimal degrees, and then convert to a geodesy object const lat = convDDMToDD(splitLat[0], splitLat[1], 10); const long = convDDMToDD(splitLong[0], splitLong[1], 10); latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees); } else { + // Not a pair, so only try to convert one set of co-ordinates const splitLat = splitInput(input); if (splitLat.length !== 2) { throw "Invalid co-ordinate format for Degrees Decimal Minutes."; @@ -145,6 +159,7 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli } latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLong[0]); } else { + // Not a pair, so only try to convert one set of co-ordinates const splitLat = splitInput(split[0]); if (splitLat.length !== 1) { throw "Invalid co-ordinate format for Decimal Degrees."; @@ -152,88 +167,115 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLat[0]); } } else { - throw "Invalid input co-ordinate format selected."; + throw `Unknown input format '${inFormat}'`; } // Everything is now a geodesy latlon object + // These store the latitude and longitude as decimal + if (inFormat.includes("Degrees")) { + // 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 + const dirs = input.match(/[NnEeSsWw]/g); + if (dirs && dirs.length >= 1) { + // Make positive lat/lon values with S/W directions into negative values + if (dirs[0] === "S" || dirs[0] === "W" && latlon.lat > 0) { + latlon.lat = 0 - latlon.lat; + } + if (dirs.length >= 2) { + if (dirs[1] === "S" || dirs[1] === "W" && latlon.lon > 0) { + latlon.lon = 0 - latlon.lon; + } + } + } + } + // Try to find the compass directions of the lat and long + const [latDir, longDir] = findDirs(latlon.lat + "," + latlon.lon, ","); + // Output conversions for each output format if (outFormat === "Decimal Degrees") { - conv = latlon.toString("d", precision); - if (!isPair) { - conv = conv.split(",")[0]; - } + // We could use the built in latlon.toString(), + // but this makes adjusting the output harder + const lat = convDDToDD(latlon.lat, precision); + const lon = convDDToDD(latlon.lon, precision); + convLat = lat.string; + convLon = lon.string; } else if (outFormat === "Degrees Decimal Minutes") { - conv = latlon.toString("dm", precision); - if (!isPair) { - conv = conv.split(",")[0]; - } + const lat = convDDToDDM(latlon.lat, precision); + const lon = convDDToDDM(latlon.lon, precision); + convLat = lat.string; + convLon = lon.string; } else if (outFormat === "Degrees Minutes Seconds") { - conv = latlon.toString("dms", precision); - if (!isPair) { - conv = conv.split(",")[0]; - } + const lat = convDDToDMS(latlon.lat, precision); + const lon = convDDToDMS(latlon.lon, precision); + convLat = lat.string; + convLon = lon.string; } else if (outFormat === "Geohash") { - conv = geohash.encode(latlon.lat.toString(), latlon.lon.toString(), precision); + convLat = geohash.encode(latlon.lat, latlon.lon, precision); } else if (outFormat === "Military Grid Reference System") { const utm = latlon.toUtm(); const mgrs = utm.toMgrs(); - conv = mgrs.toString(precision); + // MGRS wants a precision that's an even number between 2 and 10 + if (precision % 2 !== 0) { + precision = precision + 1; + } + if (precision > 10) { + precision = 10; + } + convLat = mgrs.toString(precision); } else if (outFormat === "Ordnance Survey National Grid") { const osng = geodesy.OsGridRef.latLonToOsGrid(latlon); if (osng.toString() === "") { throw "Could not convert co-ordinates to OS National Grid. Are the co-ordinates in range?"; } - conv = osng.toString(precision); + // OSNG wants a precision that's an even number between 2 and 10 + if (precision % 2 !== 0) { + precision = precision + 1; + } + if (precision > 10) { + precision = 10; + } + convLat = osng.toString(precision); } else if (outFormat === "Universal Transverse Mercator") { const utm = latlon.toUtm(); - conv = utm.toString(precision); + convLat = utm.toString(precision); } - if (conv === undefined) { + if (convLat === undefined) { throw "Error converting co-ordinates."; } + if (outFormat.includes("Degrees")) { - let [latDir, longDir] = findDirs(conv, outDelim); - if (inLatDir !== undefined) { - latDir = inLatDir; + // Format DD/DDM/DMS for output + // If we're outputting a compass direction, remove the negative sign + if (latDir === "S" && includeDir !== "None") { + convLat = convLat.replace("-", ""); } - if (inLongDir !== undefined) { - longDir = inLongDir; + if (longDir === "W" && includeDir !== "None") { + convLon = convLon.replace("-", ""); } - // DMS/DDM/DD - conv = conv.replace(", ", outDelim); - // Remove any directions from the current string, - // so we can put them where we want them - conv = conv.replace(/[NnEeSsWw]/g, ""); - if (includeDir !== "None") { - let outConv = ""; - if (!isPair) { - if (includeDir === "Before") { - outConv += latDir + " " + conv; - } else { - outConv += conv + " " + latDir; - } - } else { - const splitConv = conv.split(outDelim); - if (splitConv.length === 2) { - if (includeDir === "Before") { - outConv += latDir + " "; - } - outConv += splitConv[0]; - if (includeDir === "After") { - outConv += " " + latDir; - } - outConv += outDelim; - if (includeDir === "Before") { - outConv += longDir + " "; - } - outConv += splitConv[1]; - if (includeDir === "After") { - outConv += " " + longDir; - } - } + + let outConv = ""; + if (includeDir === "Before") { + outConv += latDir + " "; + } + + outConv += convLat; + if (includeDir === "After") { + outConv += " " + latDir; + } + outConv += outDelim; + if (isPair) { + if (includeDir === "Before") { + outConv += longDir + " "; } - conv = outConv; + outConv += convLon; + if (includeDir === "After") { + outConv += " " + longDir; + } + outConv += outDelim; } + conv = outConv; + } else { + conv = convLat + outDelim; } return conv; @@ -247,10 +289,11 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli function splitInput (input){ const split = []; - input.split(/[°′″'"\s]/).forEach(item => { - // Remove any character that isn't a digit + input.split(/\s+/).forEach(item => { + // Remove any character that isn't a digit, decimal point or negative sign item = item.replace(/[^0-9.-]/g, ""); if (item.length > 0){ + // Turn the item into a float split.push(parseFloat(item)); } }); @@ -266,10 +309,17 @@ function splitInput (input){ * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) */ function convDMSToDD (degrees, minutes, seconds, precision){ - const converted = new Object(); - converted.degrees = degrees + (minutes / 60) + (seconds / 3600); - converted.string = (Math.round(converted.degrees * precision) / precision) + "°"; - return converted; + const absDegrees = Math.abs(degrees); + let conv = absDegrees + (minutes / 60) + (seconds / 3600); + let outString = round(conv, precision) + "°"; + if (isNegativeZero(degrees) || degrees < 0) { + conv = -conv; + outString = "-" + outString; + } + return { + "degrees": conv, + "string": outString + }; } /** @@ -280,10 +330,17 @@ function convDMSToDD (degrees, minutes, seconds, precision){ * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) */ function convDDMToDD (degrees, minutes, precision) { - const converted = new Object(); - converted.degrees = degrees + minutes / 60; - converted.string = ((Math.round(converted.degrees * precision) / precision) + "°"); - return converted; + const absDegrees = Math.abs(degrees); + let conv = absDegrees + minutes / 60; + let outString = round(conv, precision) + "°"; + if (isNegativeZero(degrees) || degrees < 0) { + conv = -conv; + outString = "-" + outString; + } + return { + "degrees": conv, + "string": outString + }; } /** @@ -294,28 +351,34 @@ function convDDMToDD (degrees, minutes, precision) { * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) */ function convDDToDD (degrees, precision) { - const converted = new Object(); - converted.degrees = degrees; - converted.string = Math.round(converted.degrees * precision) / precision + "°"; - return converted; + return { + "degrees": degrees, + "string": round(degrees, precision) + "°" + }; } /** * Convert Decimal Degrees to Degrees Minutes Seconds * @param {number} decDegrees - The input data to be converted + * @param {number} precision - The precision which the result should be rounded to * @returns {{string: string, degrees: number, minutes: number, seconds: number}} An object containing the raw converted value as separate numbers (.degrees, .minutes, .seconds), and a formatted string version (obj.string) */ -function convDDToDMS (decDegrees) { - const degrees = Math.floor(decDegrees); - const minutes = Math.floor(60 * (decDegrees - degrees)); - const seconds = Math.round(3600 * (decDegrees - degrees) - 60 * minutes); - - const converted = new Object(); - converted.degrees = degrees; - converted.minutes = minutes; - converted.seconds = seconds; - converted.string = degrees + "° " + minutes + "' " + seconds + "\""; - return converted; +function convDDToDMS (decDegrees, precision) { + const absDegrees = Math.abs(decDegrees); + let degrees = Math.floor(absDegrees); + const minutes = Math.floor(60 * (absDegrees - degrees)), + seconds = round(3600 * (absDegrees - degrees) - 60 * minutes, precision); + let outString = degrees + "° " + minutes + "' " + seconds + "\""; + if (isNegativeZero(decDegrees) || decDegrees < 0) { + degrees = -degrees; + outString = "-" + outString; + } + return { + "degrees": degrees, + "minutes": minutes, + "seconds": seconds, + "string": outString + }; } /** @@ -325,15 +388,21 @@ function convDDToDMS (decDegrees) { * @returns {{string: string, degrees: number, minutes: number}} An object containing the raw converted value as separate numbers (.degrees, .minutes), and a formatted string version (obj.string) */ function convDDToDDM (decDegrees, precision) { - const degrees = Math.floor(decDegrees); - const minutes = decDegrees - degrees; - const decMinutes = Math.round((minutes * 60) * precision) / precision; + const absDegrees = Math.abs(decDegrees); + let degrees = Math.floor(absDegrees); + const minutes = absDegrees - degrees, + decMinutes = round(minutes * 60, precision); + let outString = degrees + "° " + decMinutes + "'"; + if (decDegrees < 0 || isNegativeZero(decDegrees)) { + degrees = -degrees; + outString = "-" + outString; + } - const converted = new Object(); - converted.degrees = degrees; - converted.minutes = decMinutes; - converted.string = degrees + "° " + decMinutes + "'"; - return converted; + return { + "degrees": degrees, + "minutes": decMinutes, + "string": outString, + }; } /** @@ -348,8 +417,9 @@ export function findDirs(input, delim) { const dirs = upperInput.match(dirExp); - if (dirExp.test(upperInput)) { - // If there's actually compass directions in the string + if (dirs) { + // If there's actually compass directions + // in the input, use these to work out the direction if (dirs.length <= 2 && dirs.length >= 1) { if (dirs.length === 2) { return [dirs[0], dirs[1]]; @@ -366,15 +436,13 @@ export function findDirs(input, delim) { if (!delim.includes("Direction")) { if (upperInput.includes(delim)) { const split = upperInput.split(delim); - if (split.length > 1) { - if (split[0] === "") { - lat = split[1]; - } else { + if (split.length >= 1) { + if (split[0] !== "") { lat = split[0]; } - if (split.length > 2) { - if (split[2] !== "") { - long = split[2]; + if (split.length >= 2) { + if (split[1] !== "") { + long = split[1]; } } } @@ -423,9 +491,9 @@ export function findDirs(input, delim) { export function findFormat (input, delim) { let testData; const mgrsPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]{1}\s?[A-HJ-NP-Z][A-HJ-NP-V]\s?[0-9\s]+/), - osngPattern = new RegExp(/^[STNHO][A-HJ-Z][0-9]+$/), + osngPattern = new RegExp(/^[A-HJ-Z]{2}\s+[0-9\s]+$/), geohashPattern = new RegExp(/^[0123456789BCDEFGHJKMNPQRSTUVWXYZ]+$/), - utmPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]\s[0-9\.]+\s?[0-9\.]+$/), + utmPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]\s[0-9.]+\s?[0-9.]+$/), degPattern = new RegExp(/[°'"]/g); input = input.trim(); if (delim !== null && delim.includes("Direction")) { @@ -452,31 +520,16 @@ export function findFormat (input, delim) { } } - // Test MGRS and Geohash + // Test non-degrees formats if (!degPattern.test(input)) { - const filteredInput = input.toUpperCase(); + const filteredInput = input.toUpperCase().replace(delim, ""); const isMgrs = mgrsPattern.test(filteredInput); const isOsng = osngPattern.test(filteredInput); const isGeohash = geohashPattern.test(filteredInput); const isUtm = utmPattern.test(filteredInput); - if (isMgrs && (isOsng || isGeohash)) { - if (filteredInput.includes("I")) { - // Only MGRS can have an i! - return "Military Grid Reference System"; - } - } if (isUtm) { return "Universal Transverse Mercator"; } - if (isOsng && isGeohash) { - // Geohash doesn't have A, L or O, but OSNG does. - const testExp = new RegExp(/[ALO]/g); - if (testExp.test(filteredInput)) { - return "Ordnance Survey National Grid"; - } else { - return "Geohash"; - } - } if (isMgrs) { return "Military Grid Reference System"; } @@ -510,7 +563,7 @@ export function findFormat (input, delim) { */ export function findDelim (input) { input = input.trim(); - const delims = [",", ";", ":", " "]; + const delims = [",", ";", ":"]; const testDir = input.match(/[NnEeSsWw]/g); if (testDir !== null && testDir.length > 0 && testDir.length < 3) { // Possibly contains a direction @@ -554,3 +607,22 @@ export function realDelim (delim) { "Colon": ":" }[delim]; } + +/** + * Returns true if a zero is negative + * @param {number} zero + */ +function isNegativeZero(zero) { + return zero === 0 && (1/zero < 0); +} + +/** + * Rounds a number to a specified number of decimal places + * @param {number} input - The number to be rounded + * @param {precision} precision - The number of decimal places the number should be rounded to + * @returns {number} + */ +function round(input, precision) { + precision = Math.pow(10, precision); + return Math.round(input * precision) / precision; +} From 445a85798b49ae6d3817d7b86baa3b5db7f4e611 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Fri, 18 Jan 2019 14:50:00 +0000 Subject: [PATCH 108/147] 8.22.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 00e57f64..75645253 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.22.0", + "version": "8.22.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 96053213..796e4ec4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.22.0", + "version": "8.22.1", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 0c14bacea7b7218a80c59cfbb43da6d453c89665 Mon Sep 17 00:00:00 2001 From: j433866 Date: Fri, 18 Jan 2019 15:07:19 +0000 Subject: [PATCH 109/147] Add button to input to allow opening of files using the file prompt. --- src/web/InputWaiter.mjs | 20 ++++++++++++++++++++ src/web/Manager.mjs | 1 + src/web/html/index.html | 4 ++++ 3 files changed, 25 insertions(+) diff --git a/src/web/InputWaiter.mjs b/src/web/InputWaiter.mjs index 37f1134a..245891f7 100755 --- a/src/web/InputWaiter.mjs +++ b/src/web/InputWaiter.mjs @@ -251,6 +251,26 @@ class InputWaiter { } } + /** + * 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]; + + 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 messages sent back by the LoaderWorker. diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index d33616a4..c446aba3 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -143,6 +143,7 @@ class Manager { this.addMultiEventListener("#input-text", "paste", this.input.inputPaste, this.input); document.getElementById("reset-layout").addEventListener("click", this.app.resetLayout.bind(this.app)); 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", "dragleave", this.input.inputDragleave, this.input); this.addListeners("#input-text,#input-file", "drop", this.input.inputDrop, this.input); diff --git a/src/web/html/index.html b/src/web/html/index.html index f03590ab..cc10f4b4 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -225,6 +225,10 @@
+ From acb8c0b5aff0322a93961f809dcf6604ad76118f Mon Sep 17 00:00:00 2001 From: j433866 Date: Fri, 18 Jan 2019 15:12:03 +0000 Subject: [PATCH 110/147] Change icon from folder to input --- src/web/html/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/html/index.html b/src/web/html/index.html index cc10f4b4..74eb0ed8 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -226,7 +226,7 @@