diff --git a/.eslintrc.json b/.eslintrc.json index 7dcb705c..495c000f 100755 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -38,6 +38,8 @@ // disable rules from base configurations "no-control-regex": "off", + "require-atomic-updates": "off", + "no-async-promise-executor": "off", // stylistic conventions "brace-style": ["error", "1tbs"], @@ -106,9 +108,6 @@ "COMPILE_TIME": false, "COMPILE_MSG": false, - "PKG_VERSION": false, - "ENVIRONMENT_IS_WORKER": false, - "ENVIRONMENT_IS_NODE": false, - "ENVIRONMENT_IS_WEB": false + "PKG_VERSION": false } } diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 6064121c..e90196e5 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -22,7 +22,7 @@ Before your contributions can be accepted, you must: * Line endings: UNIX style (\n) -## Design Principals +## Design Principles 1. If at all possible, all operations and features should be client-side and not rely on connections to an external server. This increases the utility of CyberChef on closed networks and in virtual machines that are not connected to the Internet. Calls to external APIs may be accepted if there is no other option, but not for critical components. 2. Latency should be kept to a minimum to enhance the user experience. This means that all operation code should sit on the client, rather than being loaded dynamically from a server. @@ -30,7 +30,7 @@ Before your contributions can be accepted, you must: 4. Minimise the use of large libraries, especially for niche operations that won't be used very often - these will be downloaded by everyone using the app, whether they use that operation or not (due to principal 2). -With these principals in mind, any changes or additions to CyberChef should keep it: +With these principles in mind, any changes or additions to CyberChef should keep it: - Standalone - Efficient diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 8e7dee82..e90ab51f 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,14 +1 @@ - - - - - - -### Summary - - -### Example - - - - + diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..b293da9a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: Bug report +about: Create a report to help us improve +title: 'Bug report: ' +labels: bug +assignees: '' + +--- + + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior or a link to the recipe / input used to cause the bug: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (if relevant, please complete the following information):** + - OS: [e.g. Windows] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..9a38f0e5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for the project +title: 'Feature request: ' +labels: feature +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/operation-request.md b/.github/ISSUE_TEMPLATE/operation-request.md new file mode 100644 index 00000000..48d076e6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/operation-request.md @@ -0,0 +1,16 @@ +--- +name: Operation request +about: Suggest a new operation +title: 'Operation request: ' +labels: operation +assignees: '' + +--- + + + +## Summary + +### Example Input + +### Example Output diff --git a/.gitignore b/.gitignore index b5aad5d0..6c9f300a 100755 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,8 @@ docs/* src/core/config/modules/* src/core/config/OperationConfig.json src/core/operations/index.mjs +src/node/config/OperationConfig.json +src/node/index.mjs +**/*.DS_Store tests/browser/output/* diff --git a/.travis.yml b/.travis.yml index 63d39719..e99e3903 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: node_js node_js: - lts/* +cache: npm addons: chrome: stable install: npm install @@ -11,9 +12,9 @@ script: - grunt lint - grunt test - grunt docs - - grunt node - grunt prod --msg="$COMPILE_MSG" - xvfb-run --server-args="-screen 0 1200x800x24" grunt testui + - grunt testnodeconsumer before_deploy: - grunt exec:sitemap - grunt copy:ghPages @@ -33,7 +34,7 @@ deploy: file_glob: true file: - build/prod/*.zip - - build/node/CyberChef.js + - src/node/cjs.js on: repo: gchq/CyberChef tags: true diff --git a/CHANGELOG.md b/CHANGELOG.md index ba7cb9f3..3fb1a807 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,34 @@ 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). +## [9.0.0] - 2019-07-09 +- [Multiple inputs](https://github.com/gchq/CyberChef/wiki/Multiple-Inputs) are now supported in the main web UI, allowing you to upload and process multiple files at once [@j433866] | [#566] +- A [Node.js API](https://github.com/gchq/CyberChef/wiki/Node-API) has been implemented, meaning that CyberChef can now be used as a library, either to provide specific operations, or an entire baking environment [@d98762625] | [#291] +- A [read-eval-print loop (REPL)](https://github.com/gchq/CyberChef/wiki/Node-API#repl) is also included to enable prototyping and experimentation with the API [@d98762625] | [#291] +- Light and dark Solarized themes added [@j433866] | [#566] + +### [8.38.0] - 2019-07-03 +- 'Streebog' and 'GOST hash' operations added [@MShwed] [@n1474335] | [#530] + +### [8.37.0] - 2019-07-03 +- 'CRC-8 Checksum' operation added [@MShwed] | [#591] + +### [8.36.0] - 2019-07-03 +- 'PGP Verify' operation added [@artemisbot] | [#585] + +### [8.35.0] - 2019-07-03 +- 'Sharpen Image', 'Convert Image Format' and 'Add Text To Image' operations added [@j433866] | [#515] + +### [8.34.0] - 2019-06-28 +- Various new visualisations added to the 'Entropy' operation [@MShwed] | [#535] +- Efficiency improvements made to the 'Entropy' operation for large file support [@n1474335] + +### [8.33.0] - 2019-06-27 +- 'Bzip2 Compress' operation added and 'Bzip2 Decompress' operation greatly improved [@artemisbot] | [#531] + +### [8.32.0] - 2019-06-27 +- 'Index of Coincidence' operation added [@Ge0rg3] | [#571] + ### [8.31.0] - 2019-04-12 - The downloadable version of CyberChef is now a .zip file containing separate modules rather than a single .htm file. It is still completely standalone and will not make any external network requests. This change reduces the complexity of the build process significantly. [@n1474335] @@ -130,6 +158,14 @@ All major and minor version changes will be documented in this file. Details of +[9.0.0]: https://github.com/gchq/CyberChef/releases/tag/v9.0.0 +[8.38.0]: https://github.com/gchq/CyberChef/releases/tag/v8.38.0 +[8.37.0]: https://github.com/gchq/CyberChef/releases/tag/v8.37.0 +[8.36.0]: https://github.com/gchq/CyberChef/releases/tag/v8.36.0 +[8.35.0]: https://github.com/gchq/CyberChef/releases/tag/v8.35.0 +[8.34.0]: https://github.com/gchq/CyberChef/releases/tag/v8.34.0 +[8.33.0]: https://github.com/gchq/CyberChef/releases/tag/v8.33.0 +[8.32.0]: https://github.com/gchq/CyberChef/releases/tag/v8.32.0 [8.31.0]: https://github.com/gchq/CyberChef/releases/tag/v8.31.0 [8.30.0]: https://github.com/gchq/CyberChef/releases/tag/v8.30.0 [8.29.0]: https://github.com/gchq/CyberChef/releases/tag/v8.29.0 @@ -189,6 +225,8 @@ All major and minor version changes will be documented in this file. Details of [@Cynser]: https://github.com/Cynser [@anthony-arnold]: https://github.com/anthony-arnold [@masq]: https://github.com/masq +[@Ge0rg3]: https://github.com/Ge0rg3 +[@MShwed]: https://github.com/MShwed [#95]: https://github.com/gchq/CyberChef/pull/299 [#173]: https://github.com/gchq/CyberChef/pull/173 @@ -200,6 +238,7 @@ All major and minor version changes will be documented in this file. Details of [#277]: https://github.com/gchq/CyberChef/issues/277 [#281]: https://github.com/gchq/CyberChef/pull/281 [#284]: https://github.com/gchq/CyberChef/pull/284 +[#291]: https://github.com/gchq/CyberChef/pull/291 [#294]: https://github.com/gchq/CyberChef/pull/294 [#296]: https://github.com/gchq/CyberChef/pull/296 [#298]: https://github.com/gchq/CyberChef/pull/298 @@ -229,6 +268,14 @@ All major and minor version changes will be documented in this file. Details of [#489]: https://github.com/gchq/CyberChef/pull/489 [#496]: https://github.com/gchq/CyberChef/pull/496 [#506]: https://github.com/gchq/CyberChef/pull/506 +[#515]: https://github.com/gchq/CyberChef/pull/515 [#516]: https://github.com/gchq/CyberChef/pull/516 [#525]: https://github.com/gchq/CyberChef/pull/525 +[#530]: https://github.com/gchq/CyberChef/pull/530 +[#531]: https://github.com/gchq/CyberChef/pull/531 [#533]: https://github.com/gchq/CyberChef/pull/533 +[#535]: https://github.com/gchq/CyberChef/pull/535 +[#566]: https://github.com/gchq/CyberChef/pull/566 +[#571]: https://github.com/gchq/CyberChef/pull/571 +[#585]: https://github.com/gchq/CyberChef/pull/585 +[#591]: https://github.com/gchq/CyberChef/pull/591 diff --git a/Gruntfile.js b/Gruntfile.js index 48d47bcb..e228d785 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -3,7 +3,6 @@ const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin; -const NodeExternals = require("webpack-node-externals"); const glob = require("glob"); const path = require("path"); @@ -15,6 +14,7 @@ const path = require("path"); * @license Apache-2.0 */ + module.exports = function (grunt) { grunt.file.defaultEncoding = "utf8"; grunt.file.preserveBOM = false; @@ -24,22 +24,6 @@ module.exports = function (grunt) { "A persistent task which creates a development build whenever source files are modified.", ["clean:dev", "clean:config", "exec:generateConfig", "concurrent:dev"]); - grunt.registerTask("node", - "Compiles CyberChef into a single NodeJS module.", - ["clean:node", "clean:config", "exec:generateConfig", "webpack:node", "chmod:build"]); - - grunt.registerTask("test", - "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. The prod task must already have been run.", - ["connect:prod", "exec:browserTests"]); - - grunt.registerTask("docs", - "Compiles documentation in the /docs directory.", - ["clean:docs", "jsdoc", "chmod:docs"]); - grunt.registerTask("prod", "Creates a production-ready build. Use the --msg flag to add a compile message.", [ @@ -47,6 +31,32 @@ module.exports = function (grunt) { "copy:standalone", "zip:standalone", "clean:standalone", "chmod" ]); + grunt.registerTask("node", + "Compiles CyberChef into a single NodeJS module.", + [ + "clean:node", "clean:config", "clean:nodeConfig", "exec:generateConfig", "exec:generateNodeIndex" + ]); + + grunt.registerTask("test", + "A task which runs all the operation tests in the tests directory.", + [ + "clean:config", "clean:nodeConfig", "exec:generateConfig", "exec:generateNodeIndex", + "exec:nodeTests", "exec:opTests" + ]); + + grunt.registerTask("testui", + "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("testnodeconsumer", + "A task which checks whether consuming CJS and ESM apps work with the CyberChef build", + ["exec:setupNodeConsumers", "exec:testCJSNodeConsumer", "exec:testESMNodeConsumer", "exec:testESMDeepImportNodeConsumer", "exec:teardownNodeConsumers"]); + + grunt.registerTask("docs", + "Compiles documentation in the /docs directory.", + ["clean:docs", "jsdoc", "chmod:docs"]); + + grunt.registerTask("default", "Lints the code base", ["eslint", "exec:repoSize"]); @@ -80,17 +90,9 @@ module.exports = function (grunt) { COMPILE_TIME: JSON.stringify(compileTime), COMPILE_MSG: JSON.stringify(grunt.option("compile-msg") || grunt.option("msg") || ""), PKG_VERSION: JSON.stringify(pkg.version), - ENVIRONMENT_IS_WORKER: function() { - return typeof importScripts === "function"; - }, - ENVIRONMENT_IS_NODE: function() { - return typeof process === "object" && typeof require === "function"; - }, - ENVIRONMENT_IS_WEB: function() { - return typeof window === "object"; - } }, - moduleEntryPoints = listEntryModules(); + moduleEntryPoints = listEntryModules(), + nodeConsumerTestPath = "~/tmp-cyberchef"; /** @@ -114,6 +116,7 @@ module.exports = function (grunt) { prod: ["build/prod/*"], node: ["build/node/*"], config: ["src/core/config/OperationConfig.json", "src/core/config/modules/*", "src/code/operations/index.mjs"], + nodeConfig: ["src/node/index.mjs", "src/node/config/OperationConfig.json"], docs: ["docs/*", "!docs/*.conf.json", "!docs/*.ico", "!docs/*.png"], standalone: ["build/prod/CyberChef*.html"] }, @@ -174,7 +177,7 @@ module.exports = function (grunt) { }, resolve: { alias: { - "./config/modules/OpModules": "./config/modules/Default" + "./config/modules/OpModules.mjs": "./config/modules/Default.mjs" } }, plugins: [ @@ -200,21 +203,6 @@ module.exports = function (grunt) { ] }; }, - node: { - mode: "production", - target: "node", - entry: "./src/node/index.mjs", - externals: [NodeExternals()], - output: { - filename: "CyberChef.js", - path: __dirname + "/build/node", - library: "CyberChef", - libraryTarget: "commonjs2" - }, - plugins: [ - new webpack.DefinePlugin(BUILD_CONSTANTS) - ] - } }, "webpack-dev-server": { options: { @@ -246,12 +234,9 @@ module.exports = function (grunt) { }, moduleEntryPoints), resolve: { alias: { - "./config/modules/OpModules": "./config/modules/Default" + "./config/modules/OpModules.mjs": "./config/modules/Default.mjs" } }, - output: { - globalObject: "this", - }, plugins: [ new webpack.DefinePlugin(BUILD_CONSTANTS), new HtmlWebpackPlugin({ @@ -358,7 +343,7 @@ module.exports = function (grunt) { watch: { config: { files: ["src/core/operations/**/*", "!src/core/operations/index.mjs"], - tasks: ["exec:generateConfig"] + tasks: ["exec:generateNodeIndex", "exec:generateConfig"] } }, concurrent: { @@ -390,12 +375,59 @@ module.exports = function (grunt) { "echo '--- Config scripts finished. ---\n'" ].join(";") }, + generateNodeIndex: { + command: [ + "echo '\n--- Regenerating node index ---'", + "node --experimental-modules --no-warnings --no-deprecation src/node/config/scripts/generateNodeIndex.mjs", + "echo '--- Node index generated. ---\n'" + ].join(";"), + }, opTests: { command: "node --experimental-modules --no-warnings --no-deprecation tests/operations/index.mjs" }, browserTests: { command: "./node_modules/.bin/nightwatch --env prod" - } + }, + nodeTests: { + command: "node --experimental-modules --no-warnings --no-deprecation tests/node/index.mjs" + }, + setupNodeConsumers: { + command: [ + "echo '\n--- Testing node conumers ---'", + "npm link", + `mkdir ${nodeConsumerTestPath}`, + `cp tests/node/consumers/* ${nodeConsumerTestPath}`, + `cd ${nodeConsumerTestPath}`, + "npm link cyberchef" + ].join(";"), + }, + teardownNodeConsumers: { + command: [ + `rm -rf ${nodeConsumerTestPath}`, + "echo '\n--- Node consumer tests complete ---'" + ].join(";"), + }, + testCJSNodeConsumer: { + command: [ + `cd ${nodeConsumerTestPath}`, + "node --no-warnings cjs-consumer.js", + ].join(";"), + stdout: false, + }, + testESMNodeConsumer: { + command: [ + `cd ${nodeConsumerTestPath}`, + "node --no-warnings --experimental-modules esm-consumer.mjs", + ].join(";"), + stdout: false, + }, + testESMDeepImportNodeConsumer: { + command: [ + `cd ${nodeConsumerTestPath}`, + "node --no-warnings --experimental-modules esm-deep-import-consumer.mjs", + ].join(";"), + stdout: false, + }, }, }); }; diff --git a/README.md b/README.md index e6d835a8..299b5d87 100755 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ You can use as many operations as you like in simple or complex ways. Some examp - Drag and drop - Operations can be dragged in and out of the recipe list, or reorganised. - - Files up to 500MB can be dragged over the input box to load them directly into the browser. + - Files up to 2GB can be dragged over the input box to load them directly into the browser. - Auto Bake - Whenever you modify the input or the recipe, CyberChef will automatically "bake" for you and produce the output immediately. - This can be turned off and operated manually if it is affecting performance (if the input is very large, for instance). @@ -67,7 +67,7 @@ You can use as many operations as you like in simple or complex ways. Some examp - Highlighting - When you highlight text in the input or output, the offset and length values will be displayed and, if possible, the corresponding data will be highlighted in the output or input respectively (example: [highlight the word 'question' in the input to see where it appears in the output][11]). - Save to file and load from file - - You can save the output to a file at any time or load a file by dragging and dropping it into the input field. Files up to around 500MB are supported (depending on your browser), however some operations may take a very long time to run over this much data. + - You can save the output to a file at any time or load a file by dragging and dropping it into the input field. Files up to around 2GB are supported (depending on your browser), however some operations may take a very long time to run over this much data. - CyberChef is entirely client-side - It should be noted that none of your recipe configuration or input (either text or files) is ever sent to the CyberChef web server - all processing is carried out within your browser, on your own computer. - Due to this feature, CyberChef can be compiled into a single HTML file. You can download this file and drop it into a virtual machine, share it with other people, or use it independently on your local machine. @@ -81,6 +81,10 @@ CyberChef is built to support - Mozilla Firefox 35+ - Microsoft Edge 14+ +## Node.js support + +CyberChef is built to fully support Node.js `v10` and partially supports `v12`. Named imports using a deep import specifier does not work in `v12`. For more information, see the Node API page in the project [wiki pages](https://github.com/gchq/CyberChef/wiki) + ## Contributing diff --git a/babel.config.js b/babel.config.js index f2f9c502..4e9503c4 100644 --- a/babel.config.js +++ b/babel.config.js @@ -4,19 +4,13 @@ module.exports = function(api) { return { "presets": [ ["@babel/preset-env", { - "targets": { - "chrome": 40, - "firefox": 35, - "edge": 14, - "node": "6.5" - }, "modules": false, "useBuiltIns": "entry", "corejs": 3 }] ], "plugins": [ - "babel-plugin-syntax-dynamic-import", + "dynamic-import-node", [ "babel-plugin-transform-builtin-extend", { "globals": ["Error"] diff --git a/package-lock.json b/package-lock.json index 0c045d13..5e31acdd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.31.3", + "version": "9.0.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -14,18 +14,18 @@ } }, "@babel/core": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.4.tgz", - "integrity": "sha512-lQgGX3FPRgbz2SKmhMtYgJvVzGZrmjaF4apZ2bLwofAKiSjxU0drPh4S/VasyYXwaTs+A1gvQ45BN8SQJzHsQQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.5.0.tgz", + "integrity": "sha512-6Isr4X98pwXqHvtigw71CKgmhL1etZjPs5A67jL/w0TkLM9eqmFR40YrnJvEc1WnMZFsskjsmid8bHZyxKEAnw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helpers": "^7.4.4", - "@babel/parser": "^7.4.4", + "@babel/generator": "^7.5.0", + "@babel/helpers": "^7.5.0", + "@babel/parser": "^7.5.0", "@babel/template": "^7.4.4", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4", + "@babel/traverse": "^7.5.0", + "@babel/types": "^7.5.0", "convert-source-map": "^1.1.0", "debug": "^4.1.0", "json5": "^2.1.0", @@ -36,65 +36,45 @@ }, "dependencies": { "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.0.tgz", + "integrity": "sha512-1TTVrt7J9rcG5PMjvO7VEG3FrEoEJNHxumRq66GemPmzboLWtIjjcJgk8rokuAS7IiRSpgVSu5Vb9lc99iJkOA==", "dev": true, "requires": { - "@babel/types": "^7.4.4", + "@babel/types": "^7.5.0", "jsesc": "^2.5.1", "lodash": "^4.17.11", "source-map": "^0.5.0", "trim-right": "^1.0.1" } }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.0.tgz", + "integrity": "sha512-I5nW8AhGpOXGCCNYGc+p7ExQIBxRFnS2fd/d862bNOKvmoEPjYPcfIjsfdy0ujagYOIYPczKgD9l3FsgTkAzKA==", "dev": true }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.0.tgz", + "integrity": "sha512-SnA9aLbyOCcnnbQEGwdfBggnc142h/rbqqsXcaATj2hZcegCl903pUD/lfpsNBlBSuWow/YDfRyJuWi2EPR5cg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", + "@babel/generator": "^7.5.0", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", + "@babel/parser": "^7.5.0", + "@babel/types": "^7.5.0", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.11" } }, "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.0.tgz", + "integrity": "sha512-UFpDVqRABKsW01bvw7/wSUe56uy6RXM5+VJibVVAybDGxEW25jdwiFJEf7ASvSaC7sN7rbE/l3cLp2izav+CtQ==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -118,9 +98,9 @@ "dev": true }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "source-map": { @@ -138,12 +118,12 @@ } }, "@babel/generator": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.0.tgz", - "integrity": "sha512-/v5I+a1jhGSKLgZDcmAUZ4K/VePi43eRkUs3yePW1HB1iANOD5tqJXwGSG4BZhSksP8J9ejSlwGeTiiOFZOrXQ==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", + "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", "dev": true, "requires": { - "@babel/types": "^7.4.0", + "@babel/types": "^7.4.4", "jsesc": "^2.5.1", "lodash": "^4.17.11", "source-map": "^0.5.0", @@ -186,97 +166,6 @@ "@babel/helper-hoist-variables": "^7.4.4", "@babel/traverse": "^7.4.4", "@babel/types": "^7.4.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "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" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "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 - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } } }, "@babel/helper-define-map": { @@ -288,25 +177,6 @@ "@babel/helper-function-name": "^7.1.0", "@babel/types": "^7.4.4", "lodash": "^4.17.11" - }, - "dependencies": { - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } } }, "@babel/helper-explode-assignable-expression": { @@ -346,25 +216,6 @@ "dev": true, "requires": { "@babel/types": "^7.4.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } } }, "@babel/helper-member-expression-to-functions": { @@ -397,51 +248,6 @@ "@babel/template": "^7.4.4", "@babel/types": "^7.4.4", "lodash": "^4.17.11" - }, - "dependencies": { - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } } }, "@babel/helper-optimise-call-expression": { @@ -491,97 +297,6 @@ "@babel/helper-optimise-call-expression": "^7.0.0", "@babel/traverse": "^7.4.4", "@babel/types": "^7.4.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, - "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" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "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 - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } } }, "@babel/helper-simple-access": { @@ -595,12 +310,12 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.0.tgz", - "integrity": "sha512-7Cuc6JZiYShaZnybDmfwhY4UYHzI6rlqhWjaIqbsJGsIqPimEYy5uh3akSRLMg65LSdSEnJ8a8/bWQN6u2oMGw==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", "dev": true, "requires": { - "@babel/types": "^7.4.0" + "@babel/types": "^7.4.4" } }, "@babel/helper-wrap-function": { @@ -616,76 +331,56 @@ } }, "@babel/helpers": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.4.tgz", - "integrity": "sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.5.0.tgz", + "integrity": "sha512-EgCUEa8cNwuMrwo87l2d7i2oShi8m2Q58H7h3t4TWtqATZalJYFwfL9DulRe02f3KdqM9xmMCw3v/7Ll+EiaWg==", "dev": true, "requires": { "@babel/template": "^7.4.4", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4" + "@babel/traverse": "^7.5.0", + "@babel/types": "^7.5.0" }, "dependencies": { "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.0.tgz", + "integrity": "sha512-1TTVrt7J9rcG5PMjvO7VEG3FrEoEJNHxumRq66GemPmzboLWtIjjcJgk8rokuAS7IiRSpgVSu5Vb9lc99iJkOA==", "dev": true, "requires": { - "@babel/types": "^7.4.4", + "@babel/types": "^7.5.0", "jsesc": "^2.5.1", "lodash": "^4.17.11", "source-map": "^0.5.0", "trim-right": "^1.0.1" } }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.0.tgz", + "integrity": "sha512-I5nW8AhGpOXGCCNYGc+p7ExQIBxRFnS2fd/d862bNOKvmoEPjYPcfIjsfdy0ujagYOIYPczKgD9l3FsgTkAzKA==", "dev": true }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.0.tgz", + "integrity": "sha512-SnA9aLbyOCcnnbQEGwdfBggnc142h/rbqqsXcaATj2hZcegCl903pUD/lfpsNBlBSuWow/YDfRyJuWi2EPR5cg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", + "@babel/generator": "^7.5.0", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", + "@babel/parser": "^7.5.0", + "@babel/types": "^7.5.0", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.11" } }, "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.0.tgz", + "integrity": "sha512-UFpDVqRABKsW01bvw7/wSUe56uy6RXM5+VJibVVAybDGxEW25jdwiFJEf7ASvSaC7sN7rbE/l3cLp2izav+CtQ==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -709,9 +404,9 @@ "dev": true }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "source-map": { @@ -749,9 +444,9 @@ } }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "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", @@ -777,9 +472,9 @@ } }, "@babel/parser": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.2.tgz", - "integrity": "sha512-9fJTDipQFvlfSVdD/JBtkiY0br9BtfvW2R8wo6CX/Ej2eMuV0gWPk1M67Mt3eggQvBqYW1FCEk8BN7WvGm/g5g==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", + "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { @@ -793,6 +488,16 @@ "@babel/plugin-syntax-async-generators": "^7.2.0" } }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz", + "integrity": "sha512-x/iMjggsKTFHYC6g11PL7Qy58IK8H5zqfm9e6hu4z1iH2IRyAp9u9dL80zA6R76yFovETFLKz2VJIC2iIPBuFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-dynamic-import": "^7.2.0" + } + }, "@babel/plugin-proposal-json-strings": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", @@ -804,9 +509,9 @@ } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz", - "integrity": "sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.0.tgz", + "integrity": "sha512-G1qy5EdcO3vYhbxlXjRSR2SXB8GsxYv9hoRKT1Jdn3qy/NUnFqUUnqymKZ00Pbj+3FXNh06B+BUZzecrp3sxNw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -843,6 +548,15 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz", + "integrity": "sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, "@babel/plugin-syntax-json-strings": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", @@ -880,9 +594,9 @@ } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz", - "integrity": "sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz", + "integrity": "sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", @@ -925,37 +639,11 @@ "globals": "^11.1.0" }, "dependencies": { - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - } - }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true } } }, @@ -969,9 +657,9 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz", - "integrity": "sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz", + "integrity": "sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" @@ -989,9 +677,9 @@ } }, "@babel/plugin-transform-duplicate-keys": { - "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==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz", + "integrity": "sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" @@ -1045,34 +733,37 @@ } }, "@babel/plugin-transform-modules-amd": { - "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==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz", + "integrity": "sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg==", "dev": true, "requires": { "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.0.0", + "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz", - "integrity": "sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz", + "integrity": "sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ==", "dev": true, "requires": { "@babel/helper-module-transforms": "^7.4.4", "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0" + "@babel/helper-simple-access": "^7.1.0", + "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz", - "integrity": "sha512-MSiModfILQc3/oqnG7NrP1jHaSPryO6tA2kOMmAQApz5dayPxWiHqmq4sWH2xF5LcQK56LlbKByCd8Aah/OIkQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz", + "integrity": "sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg==", "dev": true, "requires": { "@babel/helper-hoist-variables": "^7.4.4", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.0.0", + "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-modules-umd": { @@ -1086,12 +777,12 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.4.tgz", - "integrity": "sha512-Ki+Y9nXBlKfhD+LXaRS7v95TtTGYRAf9Y1rTDiE75zf8YQz4GDaWRXosMfJBXxnk88mGFjWdCRIeqDbon7spYA==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz", + "integrity": "sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg==", "dev": true, "requires": { - "regexp-tree": "^0.1.0" + "regexp-tree": "^0.1.6" } }, "@babel/plugin-transform-new-target": { @@ -1134,12 +825,12 @@ } }, "@babel/plugin-transform-regenerator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.4.tgz", - "integrity": "sha512-Zz3w+pX1SI0KMIiqshFZkwnVGUhDZzpX2vtPzfJBKQQq8WsP/Xy9DNdELWivxcKOCX/Pywge4SiEaPaLtoDT4g==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz", + "integrity": "sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA==", "dev": true, "requires": { - "regenerator-transform": "^0.13.4" + "regenerator-transform": "^0.14.0" } }, "@babel/plugin-transform-reserved-words": { @@ -1152,9 +843,9 @@ } }, "@babel/plugin-transform-runtime": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.4.4.tgz", - "integrity": "sha512-aMVojEjPszvau3NRg+TIH14ynZLvPewH4xhlCW1w6A3rkxTS1m4uwzRclYR9oS+rl/dr+kT+pzbfHuAWP/lc7Q==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.0.tgz", + "integrity": "sha512-LmPIZOAgTLl+86gR9KjLXex6P/lRz1fWEjTz6V6QZMmKie51ja3tvzdwORqhHc4RWR8TcZ5pClpRWs0mlaA2ng==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", @@ -1231,53 +922,60 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" + }, + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" } } }, "@babel/preset-env": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.4.4.tgz", - "integrity": "sha512-FU1H+ACWqZZqfw1x2G1tgtSSYSfxJLkpaUQL37CenULFARDo+h4xJoVHzRoHbK+85ViLciuI7ME4WTIhFRBBlw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.5.0.tgz", + "integrity": "sha512-/5oQ7cYg+6sH9Dt9yx5IiylnLPiUdyMHl5y+K0mKVNiW2wJ7FpU5bg8jKcT8PcCbxdYzfv6OuC63jLEtMuRSmQ==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-proposal-async-generator-functions": "^7.2.0", + "@babel/plugin-proposal-dynamic-import": "^7.5.0", "@babel/plugin-proposal-json-strings": "^7.2.0", - "@babel/plugin-proposal-object-rest-spread": "^7.4.4", + "@babel/plugin-proposal-object-rest-spread": "^7.5.0", "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", "@babel/plugin-syntax-async-generators": "^7.2.0", + "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-syntax-json-strings": "^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.4.4", + "@babel/plugin-transform-async-to-generator": "^7.5.0", "@babel/plugin-transform-block-scoped-functions": "^7.2.0", "@babel/plugin-transform-block-scoping": "^7.4.4", "@babel/plugin-transform-classes": "^7.4.4", "@babel/plugin-transform-computed-properties": "^7.2.0", - "@babel/plugin-transform-destructuring": "^7.4.4", + "@babel/plugin-transform-destructuring": "^7.5.0", "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/plugin-transform-duplicate-keys": "^7.2.0", + "@babel/plugin-transform-duplicate-keys": "^7.5.0", "@babel/plugin-transform-exponentiation-operator": "^7.2.0", "@babel/plugin-transform-for-of": "^7.4.4", "@babel/plugin-transform-function-name": "^7.4.4", "@babel/plugin-transform-literals": "^7.2.0", "@babel/plugin-transform-member-expression-literals": "^7.2.0", - "@babel/plugin-transform-modules-amd": "^7.2.0", - "@babel/plugin-transform-modules-commonjs": "^7.4.4", - "@babel/plugin-transform-modules-systemjs": "^7.4.4", + "@babel/plugin-transform-modules-amd": "^7.5.0", + "@babel/plugin-transform-modules-commonjs": "^7.5.0", + "@babel/plugin-transform-modules-systemjs": "^7.5.0", "@babel/plugin-transform-modules-umd": "^7.2.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.4.4", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.4.5", "@babel/plugin-transform-new-target": "^7.4.4", "@babel/plugin-transform-object-super": "^7.2.0", "@babel/plugin-transform-parameters": "^7.4.4", "@babel/plugin-transform-property-literals": "^7.2.0", - "@babel/plugin-transform-regenerator": "^7.4.4", + "@babel/plugin-transform-regenerator": "^7.4.5", "@babel/plugin-transform-reserved-words": "^7.2.0", "@babel/plugin-transform-shorthand-properties": "^7.2.0", "@babel/plugin-transform-spread": "^7.2.0", @@ -1285,18 +983,18 @@ "@babel/plugin-transform-template-literals": "^7.4.4", "@babel/plugin-transform-typeof-symbol": "^7.2.0", "@babel/plugin-transform-unicode-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "browserslist": "^4.5.2", - "core-js-compat": "^3.0.0", + "@babel/types": "^7.5.0", + "browserslist": "^4.6.0", + "core-js-compat": "^3.1.1", "invariant": "^2.2.2", "js-levenshtein": "^1.1.3", "semver": "^5.5.0" }, "dependencies": { "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.0.tgz", + "integrity": "sha512-UFpDVqRABKsW01bvw7/wSUe56uy6RXM5+VJibVVAybDGxEW25jdwiFJEf7ASvSaC7sN7rbE/l3cLp2izav+CtQ==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -1313,52 +1011,59 @@ } }, "@babel/runtime": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.4.tgz", - "integrity": "sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.0.tgz", + "integrity": "sha512-2xsuyZ0R0RBFwjgae5NpXk8FcfH4qovj5cEM5VEeB7KXnKqzaisIu2HSV/mCEISolJJuR4wkViUGYujA8MH9tw==", "requires": { "regenerator-runtime": "^0.13.2" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" + } } }, "@babel/runtime-corejs2": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.4.2.tgz", - "integrity": "sha512-y/Br/9uQnumcqcakkmobFqOTzYCWSS6Kuy1b2o7LTXR4lpeU0AhaOcPqIHW85LCxRWUDW5Vg0pU1KlE3YkORlg==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.4.5.tgz", + "integrity": "sha512-5yLuwzvIDecKwYMzJtiarky4Fb5643H3Ao5jwX0HrMR5oM5mn2iHH9wSZonxwNK0oAjAFUQAiOd4jT7/9Y2jMQ==", "requires": { "core-js": "^2.6.5", "regenerator-runtime": "^0.13.2" }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, "@babel/template": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.0.tgz", - "integrity": "sha512-SOWwxxClTTh5NdbbYZ0BmaBVzxzTh2tO/TeLTbF6MO6EzVhHTnff8CdBXx3mEtazFBoysmEM6GU/wF+SuSx4Fw==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", + "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.0", - "@babel/types": "^7.4.0" + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4" } }, "@babel/traverse": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.0.tgz", - "integrity": "sha512-/DtIHKfyg2bBKnIN+BItaIlEg5pjAnzHOIQe5w+rHAw/rg9g0V7T4rqPX8BJPfW11kt3koyjAnTNwCzb28Y1PA==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz", + "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.0", + "@babel/generator": "^7.4.4", "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.0", - "@babel/parser": "^7.4.0", - "@babel/types": "^7.4.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.4.5", + "@babel/types": "^7.4.4", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.11" @@ -1374,23 +1079,23 @@ } }, "globals": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", - "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } }, "@babel/types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz", - "integrity": "sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", + "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -1417,9 +1122,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1441,10 +1146,19 @@ "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" + } + }, "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1458,9 +1172,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1475,9 +1189,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1492,9 +1206,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1508,9 +1222,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1524,9 +1238,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1541,9 +1255,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1557,9 +1271,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1573,9 +1287,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1589,9 +1303,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1605,9 +1319,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1621,9 +1335,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1637,9 +1351,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1653,9 +1367,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1669,9 +1383,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1685,9 +1399,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1701,9 +1415,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1718,9 +1432,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1734,9 +1448,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1750,9 +1464,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1766,9 +1480,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1799,9 +1513,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1816,9 +1530,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1832,9 +1546,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1853,9 +1567,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, @@ -1868,16 +1582,39 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "dev": true + }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, "@types/node": { - "version": "8.10.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.45.tgz", - "integrity": "sha512-tGVTbA+i3qfXsLbq9rEq/hezaHY55QxQLeXQL2ejNgFAxxrgu8eMmYIOsRcl7hN1uTLVsKOOYacV/rcJM3sfgQ==", + "version": "12.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.10.tgz", + "integrity": "sha512-LcsGbPomWsad6wmMNv7nBLw7YYYyfdYcz6xryKYQhx89c3XXan+8Q6AJ43G5XDIaklaVkK3mE4fCb0SBvMiPSQ==", "dev": true }, "@webassemblyjs/ast": { @@ -2068,17 +1805,6 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, - "JSONSelect": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.4.0.tgz", - "integrity": "sha1-oI7cxn6z/L6Z7WMIVTRKDPKCu40=" - }, - "abab": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", - "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", - "dev": true - }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -2086,13 +1812,13 @@ "dev": true }, "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "dev": true, "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" + "mime-types": "~2.1.24", + "negotiator": "0.6.2" } }, "access-sniff": { @@ -2116,12 +1842,6 @@ "validator": "^9.4.1" }, "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", @@ -2132,9 +1852,9 @@ } }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "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", @@ -2142,46 +1862,6 @@ "supports-color": "^5.3.0" } }, - "jsdom": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", - "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", - "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", - "domexception": "^1.0.1", - "escodegen": "^1.9.1", - "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", - "parse5": "4.0.0", - "pn": "^1.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", - "w3c-hr-time": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", - "xml-name-validator": "^3.0.0" - } - }, - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -2196,33 +1876,13 @@ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==", "dev": true - }, - "whatwg-url": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", - "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } } } }, "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz", + "integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==", "dev": true }, "acorn-dynamic-import": { @@ -2232,9 +1892,9 @@ "dev": true }, "acorn-globals": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.0.tgz", - "integrity": "sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz", + "integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==", "dev": true, "requires": { "acorn": "^6.0.1", @@ -2254,9 +1914,9 @@ "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==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -2274,27 +1934,38 @@ } }, "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.5.tgz", + "integrity": "sha512-7q7gtRQDJSyuEHjuVgHoUa2VuemFiCMrfQc9Tc08XTAc4Zj/5U1buQJ0HU6i7fKjXU09SVgSmxa4sLvuvS8Iyg==", "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" + }, + "dependencies": { + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + } } }, "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.0.tgz", + "integrity": "sha1-7PAh+hCP0X37Xms4Py3SM+Mf/Fk=", "dev": true }, "ajv-keywords": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", - "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", "dev": true }, "amdefine": { @@ -2343,17 +2014,6 @@ "requires": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } } }, "aproba": { @@ -2370,6 +2030,38 @@ "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" + }, + "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": "https://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" + } + } } }, "argparse": { @@ -2439,9 +2131,9 @@ "dev": true }, "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", "dev": true }, "arrive": { @@ -2476,6 +2168,23 @@ "dev": true, "requires": { "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } } }, "assert-plus": { @@ -2497,9 +2206,9 @@ "dev": true }, "ast-types": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.12.3.tgz", - "integrity": "sha512-wJUcAfrdW+IgDoMGNz5MmcvahKgB7BwIbLupdKVVHxHNYt+HVR2k35swdYNv9aZpF8nvlkjbnkp2rrNwxGckZA==", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.2.tgz", + "integrity": "sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA==", "dev": true }, "astral-regex": { @@ -2509,15 +2218,18 @@ "dev": true }, "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "dev": true, + "requires": { + "lodash": "^4.17.10" + } }, "async-each": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.2.tgz", - "integrity": "sha512-6xrbvN0MOBKSJDdonmSSz2OwFSgxRaVtBDes26mj9KIGtDo+g9xosFRSC+i1gQh2oAN/tQ62AI/pGZGQjVOiRg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, "async-foreach": { @@ -2545,17 +2257,49 @@ "dev": true }, "autoprefixer": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.5.1.tgz", - "integrity": "sha512-KJSzkStUl3wP0D5sdMlP82Q52JLy5+atf2MHAre48+ckWkXgixmfHyWmA77wFDy6jTHU6mIgXv6hAQ2mf1PjJQ==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.0.tgz", + "integrity": "sha512-kuip9YilBqhirhHEGHaBTZKXL//xxGnzvsD0FtBQa6z+A69qZD6s/BAX9VzDF1i9VKDquTJDQaPLSEhOnL6FvQ==", "dev": true, "requires": { - "browserslist": "^4.5.4", - "caniuse-lite": "^1.0.30000957", + "browserslist": "^4.6.1", + "caniuse-lite": "^1.0.30000971", + "chalk": "^2.4.2", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", - "postcss": "^7.0.14", + "postcss": "^7.0.16", "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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.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": { @@ -2571,13 +2315,13 @@ "dev": true }, "axios": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", - "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", + "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", "dev": true, "requires": { - "follow-redirects": "^1.3.0", - "is-buffer": "^1.1.5" + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" } }, "babel-code-frame": { @@ -2591,9 +2335,9 @@ } }, "babel-eslint": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.1.tgz", - "integrity": "sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.2.tgz", + "integrity": "sha512-UdsurWPtgiPgpJ06ryUnuaSXC2s0WoSZnQmEpbAH65XZSdwowgN5MvyP7e88nW07FYXv72erVtpBkxyDVKhH1Q==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -2602,18 +2346,38 @@ "@babel/types": "^7.0.0", "eslint-scope": "3.7.1", "eslint-visitor-keys": "^1.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + } } }, "babel-loader": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.5.tgz", - "integrity": "sha512-NTnHnVRd2JnRqPC0vW+iOQWU5pchDbYXsG2E6DMXEpMfUcQKclF9gmf3G3ZMhzG7IG9ji4coL0cm+FxeWxDpnw==", + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz", + "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==", "dev": true, "requires": { "find-cache-dir": "^2.0.0", "loader-utils": "^1.0.2", "mkdirp": "^0.5.1", - "util.promisify": "^1.0.0" + "pify": "^4.0.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } } }, "babel-messages": { @@ -2624,11 +2388,14 @@ "babel-runtime": "^6.22.0" } }, - "babel-plugin-syntax-dynamic-import": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", - "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", - "dev": true + "babel-plugin-dynamic-import-node": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", + "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } }, "babel-plugin-transform-builtin-extend": { "version": "1.1.2", @@ -2649,9 +2416,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" }, "regenerator-runtime": { "version": "0.11.1", @@ -2786,9 +2553,9 @@ "dev": true }, "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", "dev": true, "requires": { "tweetnacl": "^0.14.3" @@ -2820,15 +2587,15 @@ } }, "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "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 }, "bignumber.js": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-8.1.1.tgz", - "integrity": "sha512-QD46ppGintwPGuL1KqmwhR0O+N2cZUg8JG/VzwI2e28sM9TqHjQB10lI4QAaMHVbLzwVLLAwEglpKPViWX+5NQ==" + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" }, "binary-extensions": { "version": "1.13.1", @@ -2851,9 +2618,9 @@ } }, "bluebird": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", - "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", "dev": true }, "bmp-js": { @@ -2885,49 +2652,71 @@ } }, "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", "dev": true, "requires": { - "bytes": "3.0.0", + "bytes": "3.1.0", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" }, "dependencies": { "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", "dev": true }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" } }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "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==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", "dev": true, "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", "unpipe": "1.0.0" } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true } } }, @@ -2960,22 +2749,23 @@ "dev": true }, "bootstrap": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.2.1.tgz", - "integrity": "sha512-tt/7vIv3Gm2mnd/WeDx36nfGGHleil0Wg8IeB7eMrVkY0fZ5iTaBisSh8oNANc2IBsCc6vCgCNTIM/IEN0+50Q==" + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz", + "integrity": "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag==" }, "bootstrap-colorpicker": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/bootstrap-colorpicker/-/bootstrap-colorpicker-2.5.3.tgz", - "integrity": "sha512-xdllX8LSMvKULs3b8JrgRXTvyvjkSMHHHVuHjjN5FNMqr6kRe5NPiMHFmeAFjlgDF73MspikudLuEwR28LbzLw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bootstrap-colorpicker/-/bootstrap-colorpicker-3.1.2.tgz", + "integrity": "sha512-yUWegXox8GYoXVtYdrEIN3QMOeEFbcSFGDadZu2pWmmMgTaw4ECX9T8GvYQzJkRusvK9uzi39FjE0W2k8lQECA==", "requires": { - "jquery": ">=1.10" + "bootstrap": ">=4.0", + "jquery": ">=2.1.0" } }, "bootstrap-material-design": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/bootstrap-material-design/-/bootstrap-material-design-4.1.1.tgz", - "integrity": "sha1-h0M9sL9k1qCvsPX6qoYGE0ydJtI=" + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/bootstrap-material-design/-/bootstrap-material-design-4.1.2.tgz", + "integrity": "sha512-hKeUkOM6g2DqpktvEMHrIDpQ5qupV4DSeKlJSJ60tLFQ+8tPlszVCa3JVLTV+ZFbJRMb0UA6UWTsnjW57kYNeg==" }, "brace-expansion": { "version": "1.1.11", @@ -3107,14 +2897,14 @@ } }, "browserslist": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.5.4.tgz", - "integrity": "sha512-rAjx494LMjqKnMPhFkuLmLp8JWEX0o8ADTGeAbOqaF+XCvYLreZrG5uVjnPBlAQ8REZK4pzXGvp0bWgrFtKaag==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.3.tgz", + "integrity": "sha512-CNBqTCq22RKM8wKJNowcqihHJ4SkI8CGeK7KOR9tPboXUuS5Zk5lQgzzTbs4oxD8x+6HUshZUa2OyNI9lR93bQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000955", - "electron-to-chromium": "^1.3.122", - "node-releases": "^1.1.13" + "caniuse-lite": "^1.0.30000975", + "electron-to-chromium": "^1.3.164", + "node-releases": "^1.1.23" } }, "bson": { @@ -3124,15 +2914,36 @@ "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": "5.2.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", - "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, "requires": { "base64-js": "^1.0.2", - "ieee754": "^1.1.4" + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } } }, "buffer-equal": { @@ -3163,6 +2974,12 @@ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "dev": true }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, "builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", @@ -3181,22 +2998,22 @@ "integrity": "sha1-sC2wB+83vrzCk4Skssb08PTHlsk=" }, "cacache": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", - "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.3.tgz", + "integrity": "sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA==", "dev": true, "requires": { - "bluebird": "^3.5.3", + "bluebird": "^3.5.5", "chownr": "^1.1.1", "figgy-pudding": "^3.5.1", - "glob": "^7.1.3", + "glob": "^7.1.4", "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", + "rimraf": "^2.6.3", "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.0.0" @@ -3211,6 +3028,15 @@ "yallist": "^3.0.2" } }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "y18n": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", @@ -3242,10 +3068,36 @@ "unset-value": "^1.0.0" } }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + }, + "dependencies": { + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + } + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, "callsites": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz", - "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, "camel-case": { @@ -3259,9 +3111,9 @@ } }, "camelcase": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.2.0.tgz", - "integrity": "sha512-IXFsBS2pC+X0j0N/GE7Dm7j3bsEBp+oTpb7F50dwEVX7rf3IgwO9XatnegTsDtniKCUtEJH4fSU6Asw7uoVLfQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "camelcase-keys": { @@ -3283,9 +3135,9 @@ } }, "caniuse-lite": { - "version": "1.0.30000957", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000957.tgz", - "integrity": "sha512-8wxNrjAzyiHcLXN/iunskqQnJquQQ6VX8JHfW5kLgAPRSiSuKZiNfmIkP5j7jgyXqAQBSoXyJxfnbCFS0ThSiQ==", + "version": "1.0.30000978", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000978.tgz", + "integrity": "sha512-H6gK6kxUzG6oAwg/Jal279z8pHw0BzrpZfwo/CA9FFm/vA0l8IhDfkZtepyJNE2Y4V6Dp3P3ubz6czby1/Mgsw==", "dev": true }, "caseless": { @@ -3294,19 +3146,10 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, - "catharsis": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.10.tgz", - "integrity": "sha512-l2OUaz/3PU3MZylspVFJvwHCVfWyvcduPq4lv3AzZ2pJzZCo7kNKFNyatwujD7XgvGkNAE/Jhhbh2uARNwNkfw==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, "chai-nightwatch": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/chai-nightwatch/-/chai-nightwatch-0.2.1.tgz", - "integrity": "sha512-2lprSMi72sHq2ZGyPTYUDQNsd2O4z81SicascbI4bkU54Xzk5Ofunn2CbrExADGC7jBH2D8r66X/aSEl+/agXQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/chai-nightwatch/-/chai-nightwatch-0.3.0.tgz", + "integrity": "sha512-NHpHLKQO0M7uNVJ10qlPIzHN9+6f873kYh6dYAn291a1CVESrrH6crbTJwZ3376trtzb6HPa80QYt3gMTL1o4g==", "dev": true, "requires": { "assertion-error": "1.0.0", @@ -3354,9 +3197,9 @@ } }, "chokidar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz", - "integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", + "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -3371,34 +3214,56 @@ "path-is-absolute": "^1.0.0", "readdirp": "^2.2.1", "upath": "^1.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + } } }, "chownr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", + "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==", "dev": true }, "chrome-trace-event": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", - "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", "dev": true, "requires": { "tslib": "^1.9.0" } }, "chromedriver": { - "version": "74.0.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-74.0.0.tgz", - "integrity": "sha512-xXgsq0l4gVTY9X5vuccOSVj/iEBm3Bf5MIwzSAASIRJagt4BlWw77SxQq1f4JAJ35/9Ys4NLMA/kWFbd7A/gfQ==", + "version": "75.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-75.0.1.tgz", + "integrity": "sha512-x94nKFBcvHHuDHJkRj94BuhGnLJcJE8G64yOkXrAEEmer5vjW7N7V+TF7CzVdT/Bb8G+37h6qT+zpLD9xbQ1og==", "dev": true, "requires": { - "del": "^3.0.0", + "del": "^4.1.1", "extract-zip": "^1.6.7", "mkdirp": "^0.5.1", "request": "^2.88.0", "tcp-port-used": "^1.0.1" + }, + "dependencies": { + "extract-zip": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", + "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", + "dev": true, + "requires": { + "concat-stream": "1.6.2", + "debug": "2.6.9", + "mkdirp": "0.5.1", + "yauzl": "2.4.1" + } + } } }, "cipher-base": { @@ -3411,11 +3276,6 @@ "safe-buffer": "^5.0.1" } }, - "cjson": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cjson/-/cjson-0.2.1.tgz", - "integrity": "sha1-c82KrWXZ4VBfmvF0TTt5wVJ2gqU=" - }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -3566,18 +3426,18 @@ "dev": true }, "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "requires": { "delayed-stream": "~1.0.0" } }, "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" }, "commondir": { "version": "1.0.1", @@ -3586,18 +3446,18 @@ "dev": true }, "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, "compressible": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz", - "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==", + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", + "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", "dev": true, "requires": { - "mime-db": ">= 1.38.0 < 2" + "mime-db": ">= 1.40.0 < 2" } }, "compression": { @@ -3620,6 +3480,12 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true } } }, @@ -3639,18 +3505,67 @@ "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" + }, + "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": "https://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" + } + } } }, "connect": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", - "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", "dev": true, "requires": { "debug": "2.6.9", - "finalhandler": "1.1.0", - "parseurl": "~1.3.2", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", "utils-merge": "1.0.1" + }, + "dependencies": { + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + } } }, "connect-history-api-fallback": { @@ -3687,10 +3602,13 @@ "dev": true }, "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } }, "content-type": { "version": "1.0.4", @@ -3714,9 +3632,9 @@ } }, "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", "dev": true }, "cookie-signature": { @@ -3746,34 +3664,33 @@ "dev": true }, "core-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.1.tgz", - "integrity": "sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==" + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.1.4.tgz", + "integrity": "sha512-YNZN8lt82XIMLnLirj9MhKDFZHalwzzrL9YLt6eb0T5D0EDl4IQ90IGkua8mHbnxNrkj1d8hbdizMc0Qmg1WnQ==" }, "core-js-compat": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.0.1.tgz", - "integrity": "sha512-2pC3e+Ht/1/gD7Sim/sqzvRplMiRnFQVlPpDVaHtY9l7zZP7knamr3VRD6NyGfHd84MrDC0tAM9ulNxYMW0T3g==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.1.4.tgz", + "integrity": "sha512-Z5zbO9f1d0YrJdoaQhphVAnKPimX92D6z8lCGphH89MNRxlL1prI9ExJPqVwP0/kgkQCv8c4GJGT8X16yUncOg==", "dev": true, "requires": { - "browserslist": "^4.5.4", - "core-js": "3.0.1", - "core-js-pure": "3.0.1", - "semver": "^6.0.0" + "browserslist": "^4.6.2", + "core-js-pure": "3.1.4", + "semver": "^6.1.1" }, "dependencies": { "semver": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", - "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==", "dev": true } } }, "core-js-pure": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.0.1.tgz", - "integrity": "sha512-mSxeQ6IghKW3MoyF4cz19GJ1cMm7761ON+WObSyLfTu/Jn3x7w4NwNFnrZxgl4MTSvYYepVLNuRtlB4loMwJ5g==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.1.4.tgz", + "integrity": "sha512-uJ4Z7iPNwiu1foygbcZYJsJs1jiXrTTCvxfLDXNhI/I+NHbSIEyr548y4fcsCEyWY0XgfAG/qqaunJ1SThHenA==", "dev": true }, "core-util-is": { @@ -3783,17 +3700,27 @@ "dev": true }, "cosmiconfig": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz", - "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", "dev": true, "requires": { + "import-fresh": "^2.0.0", "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "parse-json": "^4.0.0", - "require-from-string": "^2.0.1" + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" }, "dependencies": { + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -3803,6 +3730,12 @@ "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true } } }, @@ -3886,22 +3819,74 @@ "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=" }, "css-loader": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.1.tgz", - "integrity": "sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.0.0.tgz", + "integrity": "sha512-WR6KZuCkFbnMhRrGPlkwAA7SSCtwqPwpyXJAPhotYkYsc0mKU9n/fu5wufy4jl2WhBw9Ia8gUQMIp/1w98DuPw==", "dev": true, "requires": { - "camelcase": "^5.2.0", - "icss-utils": "^4.1.0", + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", "loader-utils": "^1.2.3", "normalize-path": "^3.0.0", - "postcss": "^7.0.14", + "postcss": "^7.0.17", "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^2.0.6", + "postcss-modules-local-by-default": "^3.0.2", "postcss-modules-scope": "^2.1.0", - "postcss-modules-values": "^2.0.0", - "postcss-value-parser": "^3.3.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.0.0", "schema-utils": "^1.0.0" + }, + "dependencies": { + "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 + }, + "json5": { + "version": "1.0.1", + "resolved": "https://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" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "postcss-value-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.0.tgz", + "integrity": "sha512-ESPktioptiSUchCKgggAkzdmkgzKfmp0EU8jXH+5kbIUB+unr0Y4CY9SRMvibuvYUBjNh1ACLbxqYNpdTQOteQ==", + "dev": true + }, + "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" + } + } } }, "css-select": { @@ -3917,9 +3902,9 @@ } }, "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.2.tgz", + "integrity": "sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ==", "dev": true }, "cssesc": { @@ -3929,18 +3914,26 @@ "dev": true }, "cssom": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", - "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", + "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=", "dev": true }, "cssstyle": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", - "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.3.0.tgz", + "integrity": "sha512-wXsoRfsRfsLVNaVzoKdqvEmK/5PFaEXNspVT22Ots6K/cnJdpoDKuQFw+qlMiXnmaif1OgeC466X1zISgAOcGg==", "dev": true, "requires": { - "cssom": "0.3.x" + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } } }, "ctph.js": { @@ -3970,9 +3963,9 @@ "dev": true }, "d3": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/d3/-/d3-5.9.2.tgz", - "integrity": "sha512-ydrPot6Lm3nTWH+gJ/Cxf3FcwuvesYQ5uk+j/kXEH/xbuYWYWTMAHTJQkyeuG8Y5WM5RSEYB41EctUrXQQytRQ==", + "version": "5.9.7", + "resolved": "https://registry.npmjs.org/d3/-/d3-5.9.7.tgz", + "integrity": "sha512-jENytrmdXtGPw7HuSK2S4gxRM1eUGjKvWQkQ6ct4yK+DB8SG3VcnVrwesfnsv8rIcxMUg18TafT4Q8mOZUMP4Q==", "requires": { "d3-array": "1", "d3-axis": "1", @@ -4044,9 +4037,9 @@ "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" }, "d3-color": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.2.3.tgz", - "integrity": "sha512-x37qq3ChOTLd26hnps36lexMRhNXEtVxZ4B25rL0DVdDsGQIJGB18S7y9XDwlDD6MD/ZBzITCf4JjGMM10TZkw==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.2.8.tgz", + "integrity": "sha512-yeANXzP37PHk0DbSTMNPhnJD+Nn4G//O5E825bR6fAfHH43hobSBpgB9G9oWVl9+XgUaQ4yCnsX1H+l8DoaL9A==" }, "d3-contour": { "version": "1.3.2", @@ -4110,9 +4103,9 @@ "integrity": "sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ==" }, "d3-geo": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.11.3.tgz", - "integrity": "sha512-n30yN9qSKREvV2fxcrhmHUdXP9TNH7ZZj3C/qnaoU0cVf/Ea85+yT7HY7i8ySPwkwjCNYtmKqQFTvLFngfkItQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.11.6.tgz", + "integrity": "sha512-z0J8InXR9e9wcgNtmVnPTj0TU8nhYT6lD/ak9may2PdKqXIeHUr8UbFLoCtrPYNsjv6YaLvSDQVl578k6nm7GA==", "requires": { "d3-array": "1" } @@ -4248,12 +4241,20 @@ } }, "data-uri-to-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.0.tgz", - "integrity": "sha512-YbKCNLPPP4inc0E5If4OaalBc7gpaM2MRv77Pv2VThVComLKfbGYtJcdDCViDyp1Wd4SebhHLz94vp91zbK6bw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.1.tgz", + "integrity": "sha512-OkVVLrerfAKZlW2ZZ3Ve2y65jgiWqBKsTfUIAFbn8nVbPcCZg6l6gikKlEYv0kXcmzqGm6mFq/Jf2vriuEkv8A==", "dev": true, "requires": { "@types/node": "^8.0.7" + }, + "dependencies": { + "@types/node": { + "version": "8.10.50", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.50.tgz", + "integrity": "sha512-+ZbcUwJdaBgOZpwXeT0v+gHC/jQbEfzoc9s4d0rN0JIKeQbuTrT+A2n1aQY6LpZjrLXJT7avVUqiCecCJeeZxA==", + "dev": true + } } }, "data-urls": { @@ -4265,6 +4266,25 @@ "abab": "^2.0.0", "whatwg-mimetype": "^2.2.0", "whatwg-url": "^7.0.0" + }, + "dependencies": { + "abab": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", + "dev": true + }, + "whatwg-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } } }, "date-now": { @@ -4318,9 +4338,9 @@ "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" }, "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", + "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", "dev": true }, "deep-for-each": { @@ -4353,6 +4373,13 @@ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "requires": { "object-keys": "^1.0.12" + }, + "dependencies": { + "object-keys": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==" + } } }, "define-property": { @@ -4416,24 +4443,34 @@ } }, "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", "dev": true, "requires": { + "@types/glob": "^7.1.1", "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" }, "dependencies": { "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } } } }, @@ -4537,13 +4574,27 @@ } }, "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "dev": true, "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" + "domelementtype": "~1.1.1", + "entities": "~1.1.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", + "dev": true + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + } } }, "dom-walk": { @@ -4558,9 +4609,9 @@ "dev": true }, "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.2.1.tgz", + "integrity": "sha512-SQVCLFS2E7G5CRCMdn6K9bIhRj1bS6QBWZfF0TUPh4V/BbqrQ619IdSS3/izn0FZ+9l+uODzaZjb08fjOfablA==", "dev": true }, "domexception": { @@ -4573,9 +4624,9 @@ } }, "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", "dev": true, "requires": { "domelementtype": "1" @@ -4591,6 +4642,12 @@ "domelementtype": "1" } }, + "dotenv": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz", + "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==", + "dev": true + }, "duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", @@ -4598,22 +4655,49 @@ "dev": true }, "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", + "integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==", "dev": true, "requires": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.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": "https://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" + } + } } }, - "ebnf-parser": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/ebnf-parser/-/ebnf-parser-0.1.10.tgz", - "integrity": "sha1-zR9rpHfFY4xAyX7ZtXLbW6tdgzE=" - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -4622,6 +4706,14 @@ "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" + }, + "dependencies": { + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + } } }, "ecdsa-sig-formatter": { @@ -4639,21 +4731,21 @@ "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==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.2.tgz", + "integrity": "sha512-PcW2a0tyTuPHz3tWyYqtK6r1fZ3gp+3Sop8Ph+ZYN81Ob5rwmbHEzaqs10N3BEsaGTkh/ooniXK+WwszGlc2+Q==", "dev": true }, "electron-to-chromium": { - "version": "1.3.124", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.124.tgz", - "integrity": "sha512-glecGr/kFdfeXUHOHAWvGcXrxNU+1wSO/t5B23tT1dtlvYB26GY8aHzZSWD7HqhqC800Lr+w/hQul6C5AF542w==", + "version": "1.3.175", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.175.tgz", + "integrity": "sha512-cQ0o7phcPA0EYkN4juZy/rq4XVxl/1ywQnytDT9hZTC0cHfaOBqWK2XlWp9Mk8xRGntgnmxlHTOux4HLb2ZNnA==", "dev": true }, "elliptic": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", - "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.0.tgz", + "integrity": "sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==", "dev": true, "requires": { "bn.js": "^4.4.0", @@ -4703,12 +4795,6 @@ "tapable": "^1.0.0" } }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -4729,25 +4815,24 @@ } }, "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "dev": true, "requires": { "is-arrayish": "^0.2.1" } }, "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz", + "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==", "requires": { - "es-to-primitive": "^1.2.0", + "es-to-primitive": "^1.1.1", "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" } }, "es-to-primitive": { @@ -4758,6 +4843,13 @@ "is-callable": "^1.1.4", "is-date-object": "^1.0.1", "is-symbol": "^1.0.2" + }, + "dependencies": { + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + } } }, "es6-object-assign": { @@ -4775,9 +4867,9 @@ } }, "es6-promise": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", - "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", "dev": true }, "es6-promise-polyfill": { @@ -4836,13 +4928,13 @@ } }, "eslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", - "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.0.1.tgz", + "integrity": "sha512-DyQRaMmORQ+JsWShYsSg4OPTjY56u1nCjAmICrE8vLWqyLKxhFXOthwMj1SA8xwfrv0CofLNVnqbfyhwCkaO0w==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", + "ajv": "^6.10.0", "chalk": "^2.1.0", "cross-spawn": "^6.0.5", "debug": "^4.0.1", @@ -4850,18 +4942,19 @@ "eslint-scope": "^4.0.3", "eslint-utils": "^1.3.1", "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", + "espree": "^6.0.0", "esquery": "^1.0.1", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", + "glob-parent": "^3.1.0", "globals": "^11.7.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "inquirer": "^6.2.2", - "js-yaml": "^3.13.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", "lodash": "^4.17.11", @@ -4869,7 +4962,6 @@ "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", "progress": "^2.0.0", "regexpp": "^2.0.1", "semver": "^5.5.1", @@ -4879,6 +4971,18 @@ "text-table": "^0.2.0" }, "dependencies": { + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "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" + } + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -4914,26 +5018,26 @@ "ms": "^2.1.1" } }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, "globals": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", - "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "progress": { @@ -4963,9 +5067,9 @@ } }, "eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", "dev": true, "requires": { "esrecurse": "^4.1.0", @@ -4984,6 +5088,11 @@ "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", "dev": true }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, "esmangle": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/esmangle/-/esmangle-1.0.1.tgz", @@ -5068,14 +5177,22 @@ } }, "espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.0.0.tgz", + "integrity": "sha512-lJvCS6YbCn3ImT3yKkPe0+tJ+mH6ljhGNjHQH9mRtiO6gjhVAOhVXW1yjnwqGwTkK3bGbye+hb00nFNmu0l/1Q==", "dev": true, "requires": { "acorn": "^6.0.7", "acorn-jsx": "^5.0.0", "eslint-visitor-keys": "^1.0.0" + }, + "dependencies": { + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "dev": true + } } }, "esprima": { @@ -5141,9 +5258,9 @@ "dev": true }, "eventemitter3": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", "dev": true }, "events": { @@ -5251,62 +5368,53 @@ } }, "express": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", - "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", "dev": true, "requires": { - "accepts": "~1.3.5", + "accepts": "~1.3.7", "array-flatten": "1.1.1", - "body-parser": "1.18.3", - "content-disposition": "0.5.2", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", "content-type": "~1.0.4", - "cookie": "0.3.1", + "cookie": "0.4.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "~1.1.2", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.1.1", + "finalhandler": "~1.1.2", "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "~1.1.2", "on-finished": "~2.3.0", - "parseurl": "~1.3.2", + "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", - "qs": "6.5.2", - "range-parser": "~1.2.0", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", "safe-buffer": "5.1.2", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "dependencies": { - "finalhandler": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", - "unpipe": "~1.0.0" - } + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true } } @@ -5415,15 +5523,73 @@ } }, "extract-zip": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", - "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz", + "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=", "dev": true, "requires": { - "concat-stream": "1.6.2", + "concat-stream": "1.6.0", "debug": "2.6.9", - "mkdirp": "0.5.1", + "mkdirp": "0.5.0", "yauzl": "2.4.1" + }, + "dependencies": { + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "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" + } + } } }, "extsprintf": { @@ -5498,19 +5664,58 @@ } }, "file-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", - "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.0.0.tgz", + "integrity": "sha512-roAbL6IdSGczwfXxhMi6Zq+jD4IfUpL0jWHD7fvmjdOVb7xBfdRUHe4LpBgO23VtVK5AW1OlWZo0p34Jvx3iWg==", "dev": true, "requires": { - "loader-utils": "^1.0.2", + "loader-utils": "^1.2.2", "schema-utils": "^1.0.0" + }, + "dependencies": { + "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 + }, + "json5": { + "version": "1.0.1", + "resolved": "https://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" + } + }, + "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" + } + } } }, "file-saver": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.1.tgz", - "integrity": "sha512-dCB3K7/BvAcUmtmh1DzFdv0eXSVJ9IAFt1mw3XZfAexodNRoE29l3xB2EX4wH2q8m/UTzwzEPq/ArYk98kUkBQ==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz", + "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==" }, "file-sync-cmp": { "version": "0.1.1", @@ -5559,18 +5764,26 @@ } }, "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.1", + "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", "unpipe": "~1.0.0" + }, + "dependencies": { + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + } } }, "find-cache-dir": { @@ -5582,6 +5795,24 @@ "commondir": "^1.0.1", "make-dir": "^2.0.0", "pkg-dir": "^3.0.0" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } } }, "find-up": { @@ -5626,47 +5857,84 @@ "flatted": "^2.0.0", "rimraf": "2.6.3", "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "flatted": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "dev": true }, "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", + "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", "dev": true, "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + }, + "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": "https://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" + } + } } }, "follow-redirects": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", - "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", "dev": true, "requires": { - "debug": "^3.2.6" + "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==", + "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.1.1" + "ms": "2.0.0" } - }, - "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 } } }, @@ -5739,6 +6007,38 @@ "requires": { "inherits": "^2.0.1", "readable-stream": "^2.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": "https://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" + } + } } }, "fs-extra": { @@ -5771,14 +6071,14 @@ "dev": true }, "fsevents": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", - "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" }, "dependencies": { "abbrev": { @@ -5792,7 +6092,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5816,13 +6117,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5839,19 +6142,22 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5861,13 +6167,13 @@ "optional": true }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "deep-extend": { @@ -5982,7 +6288,8 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5996,6 +6303,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6012,6 +6320,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6020,13 +6329,15 @@ "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6047,33 +6358,34 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "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, "optional": true }, "needle": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz", - "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.0.tgz", + "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", "dev": true, "optional": true, "requires": { - "debug": "^2.1.2", + "debug": "^4.1.0", "iconv-lite": "^0.4.4", "sax": "^1.2.4" } }, "node-pre-gyp": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", - "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz", + "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", "dev": true, "optional": true, "requires": { @@ -6101,16 +6413,16 @@ } }, "npm-bundled": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", - "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", "dev": true, "optional": true }, "npm-packlist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.2.0.tgz", - "integrity": "sha512-7Mni4Z8Xkx0/oegoqlcao/JpPCPEMtUvsmB0q7mgvlMinykJLSRTYuFqoQLYgGY8biuxIeiHO+QNJKbCfljewQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", + "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", "dev": true, "optional": true, "requires": { @@ -6135,7 +6447,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -6149,6 +6462,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -6244,7 +6558,8 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6261,9 +6576,9 @@ "optional": true }, "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true, "optional": true }, @@ -6286,6 +6601,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6307,6 +6623,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6355,20 +6672,22 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "dev": true, + "optional": 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 + "dev": true, + "optional": true } } }, "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -6387,30 +6706,6 @@ "xregexp": "2.0.0" }, "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, "xregexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", @@ -6474,9 +6769,9 @@ } }, "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", + "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", "dev": true, "requires": { "globule": "^1.0.0" @@ -6544,21 +6839,30 @@ } }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "readable-stream": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.2.0.tgz", - "integrity": "sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } + }, + "string_decoder": { + "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" + } } } }, @@ -6584,9 +6888,9 @@ } }, "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -6625,6 +6929,13 @@ "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": { @@ -6654,20 +6965,20 @@ } }, "globule": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", - "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", + "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", "dev": true, "requires": { "glob": "~7.1.1", - "lodash": "~4.17.10", + "lodash": "~4.17.4", "minimatch": "~3.0.2" } }, "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", + "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==", "dev": true }, "growl": { @@ -6728,6 +7039,16 @@ "resolve": "~1.1.0" } }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", @@ -6752,18 +7073,40 @@ "dev": true, "requires": { "shelljs": "^0.5.3" + }, + "dependencies": { + "shelljs": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", + "integrity": "sha1-xUmCuZbHbvDB5rWfvcWCX1txMRM=", + "dev": true + } } }, "grunt-concurrent": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/grunt-concurrent/-/grunt-concurrent-2.3.1.tgz", - "integrity": "sha1-Hj2zjM71o9oRleYdYx/n4yE0TSM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/grunt-concurrent/-/grunt-concurrent-3.0.0.tgz", + "integrity": "sha512-AgXtjUJESHEGeGX8neL3nmXBTHSj1QC48ABQ3ng2/vjuSBpDD8gKcVHSlXP71pFkIR8TQHf+eomOx6OSYSgfrA==", "dev": true, "requires": { - "arrify": "^1.0.1", - "async": "^1.2.1", - "indent-string": "^2.0.0", - "pad-stream": "^1.0.0" + "arrify": "^2.0.1", + "async": "^3.1.0", + "indent-string": "^4.0.0", + "pad-stream": "^2.0.0" + }, + "dependencies": { + "async": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.1.0.tgz", + "integrity": "sha512-4vx/aaY6j/j3Lw3fbCHNWP0pPaTCew3F6F3hYyl/tHs/ndmV1q7NW9T5yuJ2XAGwdQrP+6Wu20x06U4APo/iQQ==", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + } } }, "grunt-contrib-clean": { @@ -6774,17 +7117,6 @@ "requires": { "async": "^2.6.1", "rimraf": "^2.6.2" - }, - "dependencies": { - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - } } }, "grunt-contrib-connect": { @@ -6802,17 +7134,6 @@ "portscanner": "^2.2.0", "serve-index": "^1.9.1", "serve-static": "^1.13.2" - }, - "dependencies": { - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - } } }, "grunt-contrib-copy": { @@ -6849,13 +7170,13 @@ } }, "grunt-eslint": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/grunt-eslint/-/grunt-eslint-21.0.0.tgz", - "integrity": "sha512-HJocD9P35lpCvy6pPPCTgzBavzckrT1nt7lpqV55Vy8E6LQJv4RortXoH1jJTYhO5DYY7RPATv7Uc4383PUYqQ==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/grunt-eslint/-/grunt-eslint-22.0.0.tgz", + "integrity": "sha512-I7vIU4x/mb20fmA6TAmLx6Wzn7mfs8ZXeuk7LbP2ujKVFV7KZmJ3qXUyqe2wnD+v/74Rs5uYOZrLL8EoBmlG9Q==", "dev": true, "requires": { "chalk": "^2.1.0", - "eslint": "^5.0.0" + "eslint": "^6.0.1" }, "dependencies": { "ansi-styles": { @@ -6985,6 +7306,14 @@ "lodash": "~4.17.10", "underscore.string": "~3.3.4", "which": "~1.3.0" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + } } }, "grunt-retro": { @@ -7014,19 +7343,19 @@ } }, "gzip-size": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.0.0.tgz", - "integrity": "sha512-5iI7omclyqrnWw4XbXAmGhPsABkSIDQonv2K0h61lybgofWa6iZyvrI3r2zsJH4P8Nb64fFVzlvfhs0g7BBxAA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", "dev": true, "requires": { "duplexer": "^0.1.1", - "pify": "^3.0.0" + "pify": "^4.0.1" }, "dependencies": { "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true } } @@ -7054,11 +7383,11 @@ } }, "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", "requires": { - "function-bind": "^1.1.1" + "function-bind": "^1.0.2" } }, "has-ansi": { @@ -7107,6 +7436,12 @@ "kind-of": "^4.0.0" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", @@ -7155,9 +7490,9 @@ "dev": true }, "highlight.js": { - "version": "9.15.6", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.6.tgz", - "integrity": "sha512-zozTAWM1D6sozHo8kqhfYgsac+B+q0PmsjXeyDrYIHHcBN0zTVT66+s2GW1GZv7DbyaROdLXKdabwS/WqPyIdQ==" + "version": "9.15.8", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.8.tgz", + "integrity": "sha512-RrapkKQWwE+wKdF73VsOa2RQdIoO3mxwJ4P8mhbI6KYJUraUHRKM5w5zQQKXNk0xNL4UVRdulV9SBJcmzJNzVA==" }, "hmac-drbg": { "version": "1.0.1", @@ -7183,9 +7518,9 @@ "dev": true }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", + "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", "dev": true }, "hpack.js": { @@ -7198,6 +7533,38 @@ "obuf": "^1.0.0", "readable-stream": "^2.0.1", "wbuf": "^1.1.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": "https://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" + } + } } }, "html-encoding-sniffer": { @@ -7271,12 +7638,6 @@ "util.promisify": "1.0.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": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", @@ -7298,38 +7659,11 @@ } }, "html_codesniffer": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/html_codesniffer/-/html_codesniffer-2.3.0.tgz", - "integrity": "sha512-UrW8GdXUnj+zDOvd+/1cnmbQ1Y0ugzunCWdSd62JjZ2spBYo7ffoRA9i4baOrEj6/O3jRk80qyHLEKh/kNjedQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/html_codesniffer/-/html_codesniffer-2.4.0.tgz", + "integrity": "sha512-4LU3IaTLS7hMhueYE6a6G+QuwFkIA9S+V9KCXttnJ9YnJ/Kpl+L7R7aH+nohw1jaf0KjaHqQ7Y2uXgsWNIIxQA==", "dev": true }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dev": true, - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.2.0.tgz", - "integrity": "sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -7348,18 +7682,18 @@ "statuses": ">= 1.4.0 < 2" }, "dependencies": { - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true } } }, "http-parser-js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", - "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==", + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", + "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", "dev": true }, "http-proxy": { @@ -7443,9 +7777,9 @@ } }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } @@ -7482,25 +7816,72 @@ "safer-buffer": ">= 2.1.2 < 3" } }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", - "dev": true - }, "icss-utils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.0.tgz", - "integrity": "sha512-3DEun4VOeMvSczifM3F2cKQrDQ5Pj6WKhkOq6HD4QTnDUAq8MQRxy5TX6Sy1iY6WPBe4gQ3p5vTECjbIkglkkQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", "dev": true, "requires": { "postcss": "^7.0.14" + }, + "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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "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": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==" }, "iferr": { "version": "0.1.5", @@ -7597,12 +7978,6 @@ "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", "dev": true }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -7614,9 +7989,9 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "ini": { @@ -7636,9 +8011,9 @@ } }, "inquirer": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", - "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.4.1.tgz", + "integrity": "sha512-/Jw+qPZx4EDYsaT6uz7F4GJRNFMRdKNeUZw3ZnKV8lyuUgz/YWRCSUAJMZSVhSq4Ec0R2oYnyi6b3d4JXcL5Nw==", "dev": true, "requires": { "ansi-escapes": "^3.2.0", @@ -7652,7 +8027,7 @@ "run-async": "^2.2.0", "rxjs": "^6.4.0", "string-width": "^2.1.0", - "strip-ansi": "^5.0.0", + "strip-ansi": "^5.1.0", "through": "^2.3.6" }, "dependencies": { @@ -7710,14 +8085,6 @@ "requires": { "default-gateway": "^4.2.0", "ipaddr.js": "^1.9.0" - }, - "dependencies": { - "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", - "dev": true - } } }, "invariant": { @@ -7747,9 +8114,9 @@ "dev": true }, "ipaddr.js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", "dev": true }, "is-accessor-descriptor": { @@ -7761,6 +8128,12 @@ "kind-of": "^3.0.2" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -7788,15 +8161,24 @@ } }, "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", "dev": true }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=" }, "is-data-descriptor": { "version": "0.1.4", @@ -7807,6 +8189,12 @@ "kind-of": "^3.0.2" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -7898,6 +8286,12 @@ "kind-of": "^3.0.2" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -7919,27 +8313,27 @@ } }, "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.1.0.tgz", + "integrity": "sha512-Sc5j3/YnM8tDeyCsVeKlm/0p95075DyLmDEIkSgQ7mXkrOX+uTCtmQFm0CYzVyJwcCCmO3k8qfJt17SxQwB5Zw==", "dev": true }, "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", "dev": true, "requires": { - "is-path-inside": "^1.0.0" + "is-path-inside": "^2.1.0" } }, "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", "dev": true, "requires": { - "path-is-inside": "^1.0.1" + "path-is-inside": "^1.0.2" } }, "is-plain-obj": { @@ -8027,9 +8421,9 @@ } }, "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true }, "isexe": { @@ -8063,58 +8457,12 @@ }, "dependencies": { "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" } } }, - "jison": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/jison/-/jison-0.4.13.tgz", - "integrity": "sha1-kEFwfWIkE2f1iDRTK58ZwsNvrHg=", - "requires": { - "JSONSelect": "0.4.0", - "cjson": "~0.2.1", - "ebnf-parser": "~0.1.9", - "escodegen": "0.0.21", - "esprima": "1.0.x", - "jison-lex": "0.2.x", - "lex-parser": "~0.1.3", - "nomnom": "1.5.2" - }, - "dependencies": { - "escodegen": { - "version": "0.0.21", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-0.0.21.tgz", - "integrity": "sha1-U9ZSz6EDA4gnlFilJmxf/HCcY8M=", - "requires": { - "esprima": "~1.0.2", - "estraverse": "~0.0.4", - "source-map": ">= 0.1.2" - } - }, - "esprima": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", - "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=" - }, - "estraverse": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-0.0.4.tgz", - "integrity": "sha1-AaCTLf7ldGhKWYr1pnw7+bZCjbI=" - } - } - }, - "jison-lex": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/jison-lex/-/jison-lex-0.2.1.tgz", - "integrity": "sha1-rEuBXozOUTLrErXfz+jXB7iETf4=", - "requires": { - "lex-parser": "0.1.x", - "nomnom": "1.5.2" - } - }, "jpeg-js": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.5.tgz", @@ -8126,9 +8474,9 @@ "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==" }, "js-base64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", - "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", + "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": { @@ -8153,9 +8501,9 @@ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" }, "js-yaml": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.0.tgz", - "integrity": "sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -8171,45 +8519,36 @@ "xmlcreate": "^2.0.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, "jsdoc": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.1.tgz", - "integrity": "sha512-mMMsst31b8c7/Z6ewnO6ORIdVMwsobg1enX9b/2XAzW8mM3KuMANRWcMD1KMBq91IAUMOIhC5NsXu7xvNQrRyQ==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.3.tgz", + "integrity": "sha512-Yf1ZKA3r9nvtMWHO1kEuMZTlHOF8uoQ0vyo5eH7SQy5YeIiHM+B0DgKnn+X6y6KDYZcF7G2SPkKF+JORCXWE/A==", "dev": true, "requires": { "@babel/parser": "^7.4.4", "bluebird": "^3.5.4", - "catharsis": "^0.8.10", + "catharsis": "^0.8.11", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.0", "klaw": "^3.0.0", "markdown-it": "^8.4.2", "markdown-it-anchor": "^5.0.2", - "marked": "^0.6.2", + "marked": "^0.7.0", "mkdirp": "^0.5.1", - "requizzle": "^0.2.2", + "requizzle": "^0.2.3", "strip-json-comments": "^3.0.1", "taffydb": "2.6.2", "underscore": "~1.9.1" }, "dependencies": { - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", - "dev": true - }, - "bluebird": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", - "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", - "dev": true + "catharsis": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz", + "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } }, "escape-string-regexp": { "version": "2.0.0", @@ -8226,6 +8565,21 @@ "graceful-fs": "^4.1.9" } }, + "marked": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", + "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", + "dev": true + }, + "requizzle": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", + "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, "strip-json-comments": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", @@ -8256,6 +8610,54 @@ "integrity": "sha1-hCRCjVtWOtjFx/vsB5uaiwnI3Po=", "dev": true }, + "jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "abab": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", + "dev": true + }, + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + } + } + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -8292,9 +8694,9 @@ "dev": true }, "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", "dev": true }, "json5": { @@ -8304,14 +8706,6 @@ "dev": true, "requires": { "minimist": "^1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } } }, "jsonfile": { @@ -8324,12 +8718,11 @@ } }, "jsonpath": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.0.1.tgz", - "integrity": "sha512-HY5kSg82LHIs0r0h9gYBwpNc1w1qGY0qJ7al01W1bJltsN2lp+mjjA/a79gXWuvD6Xf8oPkD2d5uKMZQXTGzqA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.0.2.tgz", + "integrity": "sha512-rmzlgFZiQPc6q4HDyK8s9Qb4oxBnI5sF61y/Co5PV0lc3q2bIuRsNdueVbhoSHdKM4fxeimphOAtfz47yjCfeA==", "requires": { "esprima": "1.2.2", - "jison": "0.4.13", "static-eval": "2.0.2", "underscore": "1.7.0" }, @@ -8359,9 +8752,9 @@ }, "dependencies": { "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==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -8424,9 +8817,9 @@ } }, "kbpgp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/kbpgp/-/kbpgp-2.1.0.tgz", - "integrity": "sha512-Z6x3/1uh0jtPoTBOo+ngwEHHHgOSQHs5Yd/wg6fQPsBvLAHSd8w4sPFq1a9yzwj37IeR/WI5tkcyAngjrHaT3Q==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/kbpgp/-/kbpgp-2.1.2.tgz", + "integrity": "sha512-zEW2z0ATF7wiAp0SUAZ3Ufl5MkKj219RM02zsCwIT4G8EZmD8NAbEp7p7ccOZzGvWyi5bO0oJrF94YuY8b5XhA==", "requires": { "bn": "^1.0.0", "bzip-deflate": "^1.0.0", @@ -8441,13 +8834,6 @@ "purepack": ">=1.0.4", "triplesec": "^4.0.3", "tweetnacl": "^0.13.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } } }, "kew": { @@ -8519,10 +8905,10 @@ "type-check": "~0.3.2" } }, - "lex-parser": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/lex-parser/-/lex-parser-0.1.4.tgz", - "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" + "libbzip2-wasm": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/libbzip2-wasm/-/libbzip2-wasm-0.0.4.tgz", + "integrity": "sha512-RqscTx95+RTKhFAyjedsboR0Lmo3zd8//EuRwQXkdWmsCwYlzarVRaiYg6kS1O8m10MCQkGdrnlK9L4eAmZUwA==" }, "libyara-wasm": { "version": "0.0.12", @@ -8557,6 +8943,13 @@ "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-json-file": { @@ -8587,29 +8980,20 @@ "dev": true }, "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true, "requires": { - "big.js": "^5.2.2", + "big.js": "^3.1.3", "emojis-list": "^2.0.0", - "json5": "^1.0.1" + "json5": "^0.5.0" }, "dependencies": { "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true } } @@ -8625,9 +9009,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash._arraycopy": { "version": "3.0.0", @@ -8695,6 +9079,12 @@ "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", "dev": true }, + "lodash.chunk": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz", + "integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw=", + "dev": true + }, "lodash.clone": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-3.0.3.tgz", @@ -8713,9 +9103,9 @@ "dev": true }, "lodash.defaultsdeep": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.0.tgz", - "integrity": "sha1-vsECT4WxvZbL6kBbI8FK1kQ6b4E=", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", + "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", "dev": true }, "lodash.escaperegexp": { @@ -8784,15 +9174,15 @@ } }, "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==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "lodash.mergewith": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", - "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "dev": true }, "lodash.once": { @@ -8800,6 +9190,12 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, + "lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=", + "dev": true + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -8813,9 +9209,9 @@ "dev": true }, "loglevel": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", - "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=" + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.3.tgz", + "integrity": "sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA==" }, "loglevel-message-prefix": { "version": "3.0.0", @@ -8832,11 +9228,11 @@ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" + "js-tokens": "^3.0.0" } }, "loud-rejection": { @@ -8856,25 +9252,15 @@ "dev": true }, "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", "dev": true, "requires": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" } }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, "mamacro": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", @@ -8922,18 +9308,20 @@ "linkify-it": "^2.0.0", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + } } }, "markdown-it-anchor": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.0.2.tgz", - "integrity": "sha512-AFM/woBI8QDJMS/9+MmsBMT5/AR+ImfOsunQZTZhzcTmna3rIzAzbOh5E0l6mlFM/i9666BpUtkqQ9bS7WApCg==", - "dev": true - }, - "marked": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.6.2.tgz", - "integrity": "sha512-LqxwVH3P/rqKX4EKGz7+c2G9r98WeM/SW34ybhgNGhUQNKtf1GmmSkJ6cDGJ/t6tiyae49qRkpyTw2B9HOrgUA==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.2.4.tgz", + "integrity": "sha512-n8zCGjxA3T+Mx1pG8HEgbJbkB8JFUuRkeTZQuIM8iPY6oQ8sWOPRZJDFC9a/pNg2QkHEjjGkhBEl/RSyzaDZ3A==", "dev": true }, "md5.js": { @@ -8986,6 +9374,38 @@ "requires": { "errno": "^0.1.3", "readable-stream": "^2.0.1" + }, + "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": "https://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" + } + } } }, "meow": { @@ -9004,14 +9424,6 @@ "read-pkg-up": "^1.0.1", "redent": "^1.0.0", "trim-newlines": "^1.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } } }, "merge-descriptors": { @@ -9057,24 +9469,19 @@ "brorand": "^1.0.1" } }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", "dev": true }, "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", "dev": true, "requires": { - "mime-db": "~1.38.0" + "mime-db": "1.40.0" } }, "mimic-fn": { @@ -9092,15 +9499,28 @@ } }, "mini-css-extract-plugin": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.6.0.tgz", - "integrity": "sha512-79q5P7YGI6rdnVyIAV4NXpBQJFWdkzJxCim3Kog4078fM0piAaFlwocqbejdWtLW1cEzCexPrh6EdyFsPgVdAw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.7.0.tgz", + "integrity": "sha512-RQIw6+7utTYn8DBGsf/LpRgZCJMpZt+kuawJ/fju0KiOL6nAaTBNmCJwS7HtwSCXfS47gCkmtBFS7HdsquhdxQ==", "dev": true, "requires": { "loader-utils": "^1.1.0", - "normalize-url": "^2.0.1", + "normalize-url": "1.9.1", "schema-utils": "^1.0.0", "webpack-sources": "^1.1.0" + }, + "dependencies": { + "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" + } + } } }, "minimalistic-assert": { @@ -9125,9 +9545,9 @@ } }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "mississippi": { "version": "3.0.0", @@ -9160,9 +9580,9 @@ } }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -9204,6 +9624,13 @@ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } } }, "mkpath": { @@ -9366,10 +9793,11 @@ "dev": true }, "nan": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", - "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", - "dev": true + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": true }, "nanomatch": { "version": "1.2.13", @@ -9403,15 +9831,15 @@ "dev": true }, "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", "dev": true }, "neo-async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", - "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "dev": true }, "netmask": { @@ -9432,20 +9860,21 @@ "dev": true }, "nightwatch": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/nightwatch/-/nightwatch-1.0.19.tgz", - "integrity": "sha512-Dl+EN4wFp927nn7KRkCIJ7b0Th9PVjiwflzqsoqJOwLPcLuzSBz4FYBvHXQtUkaL4/nELVgXurw/KXqj2gcFSg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/nightwatch/-/nightwatch-1.1.13.tgz", + "integrity": "sha512-p/EqaCnVqAvGB5uaKgBhPaXCT/gxspVjgDyFRp2NmQiNnmlpTpw2wun+O5eYjF/iYDtXra7FD7uUFFLBAESvNw==", "dev": true, "requires": { "assertion-error": "^1.1.0", - "chai-nightwatch": "0.2.1", + "chai-nightwatch": "^0.3.0", + "dotenv": "7.0.0", "ejs": "^2.5.9", "lodash.clone": "3.0.3", "lodash.defaultsdeep": "^4.6.0", "lodash.merge": "^4.6.1", "minimatch": "3.0.4", "mkpath": "1.0.0", - "mocha": "^5.1.1", + "mocha": "^5.2.0", "optimist": "^0.6.1", "proxy-agent": "^3.0.0" } @@ -9460,9 +9889,9 @@ } }, "node-forge": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.2.tgz", - "integrity": "sha512-mXQ9GBq1N3uDCyV1pdSzgIguwgtVpM7f5/5J4ipz12PKWElmPpVWLDuWl8iXmhysr21+WmX/OJ5UKx82wjomgg==" + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.5.tgz", + "integrity": "sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q==" }, "node-gyp": { "version": "3.8.0", @@ -9509,9 +9938,9 @@ } }, "node-libs-browser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", - "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", "dev": true, "requires": { "assert": "^1.1.1", @@ -9524,7 +9953,7 @@ "events": "^3.0.0", "https-browserify": "^1.0.0", "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", + "path-browserify": "0.0.1", "process": "^0.11.10", "punycode": "^1.2.4", "querystring-es3": "^0.2.0", @@ -9536,20 +9965,9 @@ "tty-browserify": "0.0.0", "url": "^0.11.0", "util": "^0.11.0", - "vm-browserify": "0.0.4" + "vm-browserify": "^1.0.1" }, "dependencies": { - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, "events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", @@ -9562,6 +9980,18 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -9574,6 +10004,41 @@ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "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" + }, + "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.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" + } + }, "timers-browserify": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", @@ -9582,15 +10047,6 @@ "requires": { "setimmediate": "^1.0.4" } - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "requires": { - "inherits": "2.0.3" - } } } }, @@ -9600,9 +10056,9 @@ "integrity": "sha1-9WH0WyszY1K4KXbFHMoRR9U5N/U=" }, "node-releases": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.14.tgz", - "integrity": "sha512-d58EpVZRhQE60kWiWUaaPlK9dyC4zg3ZoMcHcky2d4hDksyQj0rUozwInOl0C66mBsqo01Tuns8AvxnL5S7PKg==", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.23.tgz", + "integrity": "sha512-uq1iL79YjfYC0WXoHbC/z28q/9pOl8kSHaXdWmAAc8No+bDwqkZbzIJz55g/MUsPgSGm9LZ7QSUbzTcH5tz47w==", "dev": true, "requires": { "semver": "^5.3.0" @@ -9642,6 +10098,12 @@ "lru-cache": "^4.0.1", "which": "^1.2.9" } + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true } } }, @@ -9650,27 +10112,6 @@ "resolved": "https://registry.npmjs.org/nodom/-/nodom-2.2.0.tgz", "integrity": "sha512-+W3jlsobV3NNkO15xQXkWoboeq1RPa/SKi8NMHmWF33SCMX4ALcM5dpPLEnUs69Gu+uZoCX9wcWXy866LXvd8w==" }, - "nomnom": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.5.2.tgz", - "integrity": "sha1-9DRUSKhTz71cDSYyDyR3qwUm/i8=", - "requires": { - "colors": "0.5.x", - "underscore": "1.1.x" - }, - "dependencies": { - "colors": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", - "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" - }, - "underscore": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz", - "integrity": "sha1-QLq4S60Z0jAJbo1u9ii/8FXYPbA=" - } - } - }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -9681,22 +10122,25 @@ } }, "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", + "is-builtin-module": "^1.0.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } }, "normalize-range": { "version": "0.1.2", @@ -9705,14 +10149,15 @@ "dev": true }, "normalize-url": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", - "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", "dev": true, "requires": { - "prepend-http": "^2.0.0", - "query-string": "^5.0.1", - "sort-keys": "^2.0.0" + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" } }, "notepack.io": { @@ -9768,85 +10213,10 @@ "integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ==" }, "nwsapi": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.2.tgz", - "integrity": "sha512-TQOQNxqEdxVjwgwNZyvKDF0vALmzQKZJEZwE3fZWDb7Ns5Hw6l9PxJTGKOHZGsmf7R6grsOe8lWxI43Clz79zg==", - "dev": true, - "requires": { - "jsdom": "^14.0.0" - }, - "dependencies": { - "jsdom": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-14.0.0.tgz", - "integrity": "sha512-/VkyPmdtbwqpJSkwDx3YyJ3U1oawYNB/h5z8vTUZGAzjtu2OHTeFRfnJqyMHsJ5Cyes23trOmvUpM1GfHH1leA==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "acorn": "^6.0.4", - "acorn-globals": "^4.3.0", - "array-equal": "^1.0.0", - "cssom": "^0.3.4", - "cssstyle": "^1.1.1", - "data-urls": "^1.1.0", - "domexception": "^1.0.1", - "escodegen": "^1.11.0", - "html-encoding-sniffer": "^1.0.2", - "nwsapi": "^2.0.9", - "parse5": "5.1.0", - "pn": "^1.1.0", - "request": "^2.88.0", - "request-promise-native": "^1.0.5", - "saxes": "^3.1.5", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.5.0", - "w3c-hr-time": "^1.0.1", - "w3c-xmlserializer": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^7.0.0", - "ws": "^6.1.2", - "xml-name-validator": "^3.0.0" - } - }, - "parse5": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", - "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", - "dev": true - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - } - } + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", + "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==", + "dev": true }, "oauth-sign": { "version": "0.9.0", @@ -9880,6 +10250,12 @@ "is-descriptor": "^0.1.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -9892,9 +10268,10 @@ } }, "object-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", - "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true }, "object-visit": { "version": "1.0.1", @@ -9905,6 +10282,18 @@ "isobject": "^3.0.0" } }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, "object.getownpropertydescriptors": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", @@ -9945,9 +10334,9 @@ } }, "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", "dev": true }, "once": { @@ -9975,9 +10364,9 @@ "dev": true }, "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", + "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==", "dev": true, "requires": { "is-wsl": "^1.1.0" @@ -9993,6 +10382,12 @@ "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", @@ -10105,15 +10500,24 @@ } }, "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", "dev": true }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "requires": { + "retry": "^0.12.0" + } + }, "p-try": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.1.0.tgz", - "integrity": "sha512-H2RyIJ7+A3rjkwKC2l5GGtU4H1vkxKCAGsWasNVd0Set+6i4znxbWy6/j16YDPJDWxhsgZiKAstMEP8wCdSpjA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "pac-proxy-agent": { @@ -10133,9 +10537,9 @@ }, "dependencies": { "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", "dev": true }, "debug": { @@ -10147,32 +10551,42 @@ "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==", + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" } }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "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==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", + "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==", "dev": true, "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", + "bytes": "3.1.0", + "http-errors": "1.7.3", + "iconv-lite": "0.4.24", "unpipe": "1.0.0" } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true } } }, @@ -10190,22 +10604,20 @@ } }, "pad-stream": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pad-stream/-/pad-stream-1.2.0.tgz", - "integrity": "sha1-Yx3Mn3mBC3BZZeid7eps/w/B38k=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pad-stream/-/pad-stream-2.0.0.tgz", + "integrity": "sha1-O+vzTNpJWXISpmny/kF9ZGp8ulY=", "dev": true, "requires": { - "meow": "^3.0.0", "pumpify": "^1.3.3", - "repeating": "^2.0.0", - "split2": "^1.0.0", + "split2": "^2.1.1", "through2": "^2.0.0" } }, "pako": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" }, "parallel-transform": { "version": "1.1.0", @@ -10216,6 +10628,38 @@ "cyclist": "~0.2.2", "inherits": "^2.0.3", "readable-stream": "^2.1.5" + }, + "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": "https://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" + } + } } }, "param-case": { @@ -10287,10 +10731,16 @@ "error-ex": "^1.2.0" } }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true }, "pascalcase": { @@ -10300,9 +10750,9 @@ "dev": true }, "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", "dev": true }, "path-dirname": { @@ -10423,9 +10873,9 @@ "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==" }, "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, "pinkie": { @@ -10491,6 +10941,14 @@ "async": "^1.5.2", "debug": "^2.2.0", "mkdirp": "0.5.x" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + } } }, "portscanner": { @@ -10501,17 +10959,6 @@ "requires": { "async": "^2.6.0", "is-number-like": "^1.0.3" - }, - "dependencies": { - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - } } }, "posix-character-classes": { @@ -10521,9 +10968,9 @@ "dev": true }, "postcss": { - "version": "7.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", - "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", + "version": "7.0.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.17.tgz", + "integrity": "sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -10574,9 +11021,9 @@ } }, "postcss-css-variables": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/postcss-css-variables/-/postcss-css-variables-0.12.0.tgz", - "integrity": "sha512-fSgIfR+g/kZ2GeV3GH7wyNslDi7KH/Z+zwtHdEcmHv86mu+o71va9r9rqFLPR0VKi2BzoQZbCyw89oXK/XevIQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/postcss-css-variables/-/postcss-css-variables-0.13.0.tgz", + "integrity": "sha512-gji53rkQx8UcNHpAPgn+kdRs3ZQuPk2Ebo+HDVMIaU4lo9xt7i46X7rvGJDSwR259V1RlPc6vMQdybgsgKtqKA==", "dev": true, "requires": { "escape-string-regexp": "^1.0.3", @@ -10639,12 +11086,12 @@ } }, "postcss-load-config": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz", - "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", + "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", "dev": true, "requires": { - "cosmiconfig": "^4.0.0", + "cosmiconfig": "^5.0.0", "import-cwd": "^2.0.0" } }, @@ -10658,6 +11105,19 @@ "postcss": "^7.0.0", "postcss-load-config": "^2.0.0", "schema-utils": "^1.0.0" + }, + "dependencies": { + "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" + } + } } }, "postcss-modules-extract-imports": { @@ -10670,14 +11130,23 @@ } }, "postcss-modules-local-by-default": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz", - "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz", + "integrity": "sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==", "dev": true, "requires": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0", - "postcss-value-parser": "^3.3.1" + "icss-utils": "^4.1.1", + "postcss": "^7.0.16", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.0.tgz", + "integrity": "sha512-ESPktioptiSUchCKgggAkzdmkgzKfmp0EU8jXH+5kbIUB+unr0Y4CY9SRMvibuvYUBjNh1ACLbxqYNpdTQOteQ==", + "dev": true + } } }, "postcss-modules-scope": { @@ -10688,16 +11157,122 @@ "requires": { "postcss": "^7.0.6", "postcss-selector-parser": "^6.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" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "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": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-modules-values": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz", - "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", "dev": true, "requires": { - "icss-replace-symbols": "^1.1.0", + "icss-utils": "^4.0.0", "postcss": "^7.0.6" + }, + "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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "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": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "postcss-selector-parser": { @@ -10723,9 +11298,9 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, "pretty-error": { @@ -10744,15 +11319,10 @@ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, - "process": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" - }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "progress": { @@ -10778,16 +11348,53 @@ "revalidator": "0.1.x", "utile": "0.3.x", "winston": "2.1.x" + }, + "dependencies": { + "async": { + "version": "1.0.0", + "resolved": "http://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", + "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", + "dev": true, + "requires": { + "async": "~1.0.0", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "pkginfo": "0.3.x", + "stack-trace": "0.0.x" + }, + "dependencies": { + "colors": { + "version": "1.0.3", + "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true + }, + "pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=", + "dev": true + } + } + } } }, "proxy-addr": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", "dev": true, "requires": { "forwarded": "~0.1.2", - "ipaddr.js": "1.8.0" + "ipaddr.js": "1.9.0" } }, "proxy-agent": { @@ -10816,9 +11423,9 @@ } }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } @@ -10842,9 +11449,9 @@ "dev": true }, "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.33.tgz", + "integrity": "sha512-LTDP2uSrsc7XCb5lO7A8BI1qYxRe/8EqlRvMeEl6rsnYAqDOl8xHR+8lSAIVfrNaSAlTPTNOCgNjWcoUL3AZsw==", "dev": true }, "public-encrypt": { @@ -10885,8 +11492,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "purepack": { "version": "1.0.5", @@ -10905,12 +11511,11 @@ "dev": true }, "query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", "dev": true, "requires": { - "decode-uri-component": "^0.2.0", "object-assign": "^4.1.0", "strict-uri-encode": "^1.0.0" } @@ -10953,9 +11558,9 @@ } }, "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true }, "raw-body": { @@ -10966,34 +11571,18 @@ "requires": { "bytes": "1", "string_decoder": "0.10" - }, - "dependencies": { - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } } }, "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz", + "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "dev": true, "requires": { - "deep-extend": "^0.6.0", + "deep-extend": "^0.5.1", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } } }, "read": { @@ -11065,18 +11654,15 @@ } }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "1.1.14", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "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.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" } }, "readdirp": { @@ -11088,6 +11674,38 @@ "graceful-fs": "^4.1.11", "micromatch": "^3.1.10", "readable-stream": "^2.0.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": "https://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" + } + } } }, "redent": { @@ -11107,9 +11725,9 @@ "dev": true }, "regenerate-unicode-properties": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.2.tgz", - "integrity": "sha512-SbA/iNrBUf6Pv2zU8Ekv1Qbhv92yxL4hiDa2siuxs4KKn4oOoMDHXjAf7+Nz9qinUQ46B1LcWEi/PhJfPWpZWQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", + "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", "dev": true, "requires": { "regenerate": "^1.4.0" @@ -11121,9 +11739,9 @@ "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" }, "regenerator-transform": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.4.tgz", - "integrity": "sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.0.tgz", + "integrity": "sha512-rtOelq4Cawlbmq9xuMR5gdFmv7ku/sFoB7sRiywx7aq53bc52b4j6zvH7Te1Vt/X2YveDKnCGUbioieU7FEL3w==", "dev": true, "requires": { "private": "^0.1.6" @@ -11140,9 +11758,9 @@ } }, "regexp-tree": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.6.tgz", - "integrity": "sha512-LFrA98Dw/heXqDojz7qKFdygZmFoiVlvE1Zp7Cq2cvF+ZA+03Gmhy0k0PQlsC1jvHPiTUSs+pDHEuSWv6+6D7w==", + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.10.tgz", + "integrity": "sha512-K1qVSbcedffwuIslMwpe6vGlj+ZXRnGkvjAtFHfDZZZuEdA/h0dxljAPu9vhUo6Rrx2U2AwJ+nSQ6hK+lrP5MQ==", "dev": true }, "regexpp": { @@ -11201,16 +11819,60 @@ "dev": true }, "renderkid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", - "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.2.tgz", + "integrity": "sha512-FsygIxevi1jSiPY9h7vZmBFUbAOcbYm9UwyiLNdVsLRs/5We9Ob5NMPbGYUTWiLq5L+ezlVdE0A8bbME5CWTpg==", "dev": true, "requires": { "css-select": "^1.1.0", - "dom-converter": "^0.2", - "htmlparser2": "^3.3.0", + "dom-converter": "~0.2", + "htmlparser2": "~3.3.0", "strip-ansi": "^3.0.0", "utila": "^0.4.0" + }, + "dependencies": { + "domhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", + "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", + "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "htmlparser2": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", + "dev": true, + "requires": { + "domelementtype": "1", + "domhandler": "2.1", + "domutils": "1.1", + "readable-stream": "1.0" + } + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + } } }, "repeat-element": { @@ -11260,6 +11922,20 @@ "tough-cookie": "~2.4.3", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" + }, + "dependencies": { + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + } } }, "request-progress": { @@ -11272,23 +11948,23 @@ } }, "request-promise-core": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", - "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", + "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", "dev": true, "requires": { - "lodash": "^4.17.11" + "lodash": "^4.13.1" } }, "request-promise-native": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", - "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", + "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", "dev": true, "requires": { - "request-promise-core": "1.1.2", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" + "request-promise-core": "1.1.1", + "stealthy-require": "^1.1.0", + "tough-cookie": ">=2.3.3" } }, "require-directory": { @@ -11297,12 +11973,6 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, "require-main-filename": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", @@ -11315,19 +11985,10 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, - "requizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.2.tgz", - "integrity": "sha512-oJ6y7JcUJkblRGhMByGNcszeLgU0qDxNKFCiUZR1XyzHyVsev+Mxb1tyygxLd1ORsKee1SA5BInFdUwY64GE/A==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", + "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -11378,6 +12039,12 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, "revalidator": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", @@ -11385,12 +12052,12 @@ "dev": true }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "^7.1.3" + "glob": "^7.0.5" } }, "ripemd160": { @@ -11427,9 +12094,9 @@ "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" }, "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", + "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -11461,9 +12128,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sanitize-html": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.0.tgz", - "integrity": "sha512-BpxXkBoAG+uKCHjoXFmox6kCSYpnulABoGcZ/R3QyY9ndXbIM5S94eOr1IqnzTG8TnbmXaxWoDDzKC5eJv7fEQ==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.1.tgz", + "integrity": "sha512-txnH8TQjaQvg2Q0HY06G6CDJLVYCpbnxrdO0WN8gjCKaU5J0KbyGYhZxx5QJg3WLZ1lB7XU9kDkfrCXUozqptA==", "dev": true, "requires": { "chalk": "^2.4.1", @@ -11488,9 +12155,9 @@ } }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "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", @@ -11498,6 +12165,102 @@ "supports-color": "^5.3.0" } }, + "domelementtype": { + "version": "1.3.0", + "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", + "dev": true + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "htmlparser2": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.0.tgz", + "integrity": "sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.0", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.0.6" + } + }, + "postcss": { + "version": "7.0.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.17.tgz", + "integrity": "sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "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" + } + } + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "readable-stream": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.0.6.tgz", + "integrity": "sha512-9E1oLoOWfhSXHGv6QlwXJim7uNzd9EVlWK+21tCU9Ju/kR0/p2AZYPz4qSchgO8PlLIH4FpZYfzwS+rEksZjIg==", + "dev": true, + "requires": { + "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==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -11533,14 +12296,6 @@ "neo-async": "^2.5.0", "pify": "^3.0.0", "semver": "^5.5.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } } }, "sax": { @@ -11548,30 +12303,20 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, - "saxes": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.9.tgz", - "integrity": "sha512-FZeKhJglhJHk7eWG5YM0z46VHmI3KJpMBAQm3xa9meDvd+wevB5GuBB0wc0exPInZiBBHqi00DbS8AcvCGCFMw==", - "dev": true, - "requires": { - "xmlchars": "^1.3.1" - } - }, "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==", + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", "dev": true, "requires": { "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", "ajv-keywords": "^3.1.0" } }, "scryptsy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.0.0.tgz", - "integrity": "sha1-Jiw28CMc+nZU4jY/o5TNLexm83g=" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", + "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==" }, "scss-tokenizer": { "version": "0.2.3", @@ -11618,14 +12363,14 @@ } }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" }, "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", "dev": true, "requires": { "debug": "2.6.9", @@ -11635,24 +12380,43 @@ "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" + "range-parser": "~1.2.1", + "statuses": "~1.5.0" }, "dependencies": { + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "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 + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true } } @@ -11679,15 +12443,15 @@ } }, "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", "dev": true, "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" + "parseurl": "~1.3.3", + "send": "0.17.1" } }, "set-blocking": { @@ -11697,9 +12461,9 @@ "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -11775,12 +12539,6 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, - "shelljs": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", - "integrity": "sha1-xUmCuZbHbvDB5rWfvcWCX1txMRM=", - "dev": true - }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -11788,20 +12546,32 @@ "dev": true }, "sitemap": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-2.2.0.tgz", - "integrity": "sha512-9Zoi3UBhSIt5jWENDRUbzsqLMJ+Fha3P2aQ2PRghmh0FOivtHsC4FAJdkAEKHvATajd74BWp/57Yh7kz/UA53Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-3.2.0.tgz", + "integrity": "sha512-tP4B5l8PjMt5e/v4kxn3jQjjHpxYe/VXZUSIMIkOmbKCvbPwNU9PEsguaJl4Q09ZJqrOQlt691Si4VuevVtw9Q==", "dev": true, "requires": { - "lodash": "^4.17.10", - "url-join": "^4.0.0", - "xmlbuilder": "^10.0.0" + "lodash.chunk": "^4.2.0", + "lodash.padstart": "^4.6.1", + "whatwg-url": "^7.0.0", + "xmlbuilder": "^13.0.0" }, "dependencies": { + "whatwg-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, "xmlbuilder": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz", - "integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", + "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==", "dev": true } } @@ -11941,6 +12711,12 @@ "kind-of": "^3.2.0" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -11986,18 +12762,18 @@ } }, "faye-websocket": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", - "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", "dev": true, "requires": { "websocket-driver": ">=0.5.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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } @@ -12020,12 +12796,32 @@ "requires": { "agent-base": "~4.2.1", "socks": "~2.3.2" + }, + "dependencies": { + "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" + } + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + } } }, "sort-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", - "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", "dev": true, "requires": { "is-plain-obj": "^1.0.0" @@ -12077,9 +12873,9 @@ "dev": true }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", + "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -12103,9 +12899,9 @@ } }, "spdx-license-ids": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", - "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", + "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", "dev": true }, "spdy": { @@ -12131,9 +12927,9 @@ } }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } @@ -12162,21 +12958,30 @@ } }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "readable-stream": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", - "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } + }, + "string_decoder": { + "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" + } } } }, @@ -12190,17 +12995,17 @@ } }, "split.js": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/split.js/-/split.js-1.5.10.tgz", - "integrity": "sha512-/J52X5c4ZypVwu4WAhD8E1T9uXQtNokvG6mIBHauzyA1aKH6bmETVSv3RPjBXEz6Gcc4mIThgmjGQL39LD16jQ==" + "version": "1.5.11", + "resolved": "https://registry.npmjs.org/split.js/-/split.js-1.5.11.tgz", + "integrity": "sha512-ec0sAbWnaMGpNHWo1ZgIlF3Mx7GzSyaO0GlcEBZGIFZQwYPPkbDV6JRpDmpzIshVig7USREuEPudy0ygQaskXg==" }, "split2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/split2/-/split2-1.1.1.tgz", - "integrity": "sha1-Fi2bGIZfAqsvKtlYVSLbm1TEgfk=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", + "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", "dev": true, "requires": { - "through2": "~2.0.0" + "through2": "^2.0.2" } }, "sprintf-js": { @@ -12225,9 +13030,9 @@ "integrity": "sha1-mItJTQ3JwxkAX9rJZj1jOO/tHyI=" }, "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz", + "integrity": "sha512-Ra/OXQtuh0/enyl4ETZAfTaeksa6BXks5ZcjpSUNrjBr0DvrJKX+1fsKDPpT9TBXgHAFsa4510aNVgI8g/+SzA==", "dev": true, "requires": { "asn1": "~0.2.3", @@ -12241,6 +13046,12 @@ "tweetnacl": "~0.14.0" }, "dependencies": { + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -12294,9 +13105,9 @@ } }, "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "dev": true }, "stdout-stream": { @@ -12306,6 +13117,38 @@ "dev": true, "requires": { "readable-stream": "^2.0.1" + }, + "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": "https://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" + } + } } }, "stealthy-require": { @@ -12322,6 +13165,38 @@ "requires": { "inherits": "~2.0.1", "readable-stream": "^2.0.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": "https://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" + } + } } }, "stream-each": { @@ -12345,6 +13220,38 @@ "readable-stream": "^2.3.6", "to-arraybuffer": "^1.0.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": "https://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" + } + } } }, "stream-shift": { @@ -12403,13 +13310,10 @@ } }, "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==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true }, "strip-ansi": { "version": "3.0.1", @@ -12457,6 +13361,27 @@ "requires": { "loader-utils": "^1.1.0", "schema-utils": "^1.0.0" + }, + "dependencies": { + "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" + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + } } }, "supports-color": { @@ -12465,70 +13390,53 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, "svg-url-loader": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/svg-url-loader/-/svg-url-loader-2.3.2.tgz", - "integrity": "sha1-3YaybBn+O5FPBOoQ7zlZTq3gRGQ=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/svg-url-loader/-/svg-url-loader-3.0.0.tgz", + "integrity": "sha512-z50VtJLRPoZKUW8gv/YP4ld3QUknK4Vzi2q9dHWYW9iFfOeOMLbBo5x61YLD8O+BrzOk1rVB7ZNCFdX83DTgFg==", "dev": true, "requires": { - "file-loader": "1.1.11", - "loader-utils": "1.1.0" + "file-loader": "4.0.0", + "loader-utils": "1.2.3" }, "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==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, - "file-loader": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", - "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", - "dev": true, - "requires": { - "loader-utils": "^1.0.2", - "schema-utils": "^0.4.5" - } - }, "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" + "minimist": "^1.2.0" } }, - "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "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": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" } } } }, "symbol-tree": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, "table": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz", - "integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.1.tgz", + "integrity": "sha512-E6CK1/pZe2N75rGZQotFOdmzWQ1AILtgYbMAbAjvms0S1l5IDB47zG3nCnFGB/w+7nB3vKofbLXCH7HPBo864w==", "dev": true, "requires": { "ajv": "^6.9.1", @@ -12537,6 +13445,18 @@ "string-width": "^3.0.0" }, "dependencies": { + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "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" + } + }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", @@ -12572,19 +13492,19 @@ "dev": true }, "tapable": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", - "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true }, "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", "dev": true, "requires": { "block-stream": "*", - "fstream": "^1.0.2", + "fstream": "^1.0.12", "inherits": "2" } }, @@ -12608,38 +13528,87 @@ } }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } }, "terser": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", - "integrity": "sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.0.2.tgz", + "integrity": "sha512-IWLuJqTvx97KP3uTYkFVn93cXO+EtlzJu8TdJylq+H0VBDlPMIfQA9MBS5Vc5t3xTEUG1q0hIfHMpAP2R+gWTw==", "dev": true, "requires": { "commander": "^2.19.0", "source-map": "~0.6.1", "source-map-support": "~0.5.10" + }, + "dependencies": { + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true + } } }, "terser-webpack-plugin": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz", - "integrity": "sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.3.0.tgz", + "integrity": "sha512-W2YWmxPjjkUcOWa4pBEv4OP4er1aeQJlSo2UhtCFQCuRXEHjOFscO8VyWHj9JLlA0RzQb8Y2/Ta78XZvT54uGg==", "dev": true, "requires": { - "cacache": "^11.0.2", + "cacache": "^11.3.2", "find-cache-dir": "^2.0.0", + "is-wsl": "^1.1.0", + "loader-utils": "^1.2.3", "schema-utils": "^1.0.0", - "serialize-javascript": "^1.4.0", + "serialize-javascript": "^1.7.0", "source-map": "^0.6.1", - "terser": "^3.16.1", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" + "terser": "^4.0.0", + "webpack-sources": "^1.3.0", + "worker-farm": "^1.7.0" + }, + "dependencies": { + "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 + }, + "json5": { + "version": "1.0.1", + "resolved": "https://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" + } + }, + "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": { @@ -12673,6 +13642,38 @@ "requires": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" + }, + "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": "https://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" + } + } } }, "thunkify": { @@ -12725,9 +13726,9 @@ } }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } @@ -12766,6 +13767,12 @@ "kind-of": "^3.0.2" }, "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -12799,6 +13806,12 @@ "repeat-string": "^1.6.1" } }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, "toposort": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", @@ -12873,9 +13886,9 @@ "dev": true }, "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", "dev": true }, "tty-browserify": { @@ -12913,13 +13926,13 @@ "dev": true }, "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.18" + "mime-types": "~2.1.24" } }, "typedarray": { @@ -12929,9 +13942,9 @@ "dev": true }, "ua-parser-js": { - "version": "0.7.19", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz", - "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==" + "version": "0.7.20", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.20.tgz", + "integrity": "sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw==" }, "uc.micro": { "version": "1.0.6", @@ -12940,11 +13953,11 @@ "dev": true }, "uglify-js": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.2.tgz", - "integrity": "sha512-imog1WIsi9Yb56yRt5TfYVxGmnWs3WSGU73ieSOlMVFwhJCA9W8fqFFMMj4kgDqiS/80LGdsYnWL7O9UcjEBlg==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", "requires": { - "commander": "~2.19.0", + "commander": "~2.20.0", "source-map": "~0.6.1" } }, @@ -12953,6 +13966,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", @@ -12997,38 +14016,15 @@ "dev": true }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "uniq": { @@ -13047,9 +14043,9 @@ } }, "unique-slug": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", - "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", "dev": true, "requires": { "imurmurhash": "^0.1.4" @@ -13062,17 +14058,6 @@ "dev": true, "requires": { "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } } }, "unpipe": { @@ -13118,6 +14103,12 @@ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true } } }, @@ -13166,38 +14157,43 @@ } } }, - "url-join": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.0.tgz", - "integrity": "sha1-TTNA6AfTdzvamZH4MFrNzCpmXSo=", - "dev": true - }, "url-loader": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", - "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-2.0.1.tgz", + "integrity": "sha512-nd+jtHG6VgYx/NnXxXSWCJ7FtHIhuyk6Pe48HKhq29Avq3r5FSdIrenvzlbb67A3SNFaQyLk0/lMZfubj0+5ww==", "dev": true, "requires": { "loader-utils": "^1.1.0", - "mime": "^2.0.3", + "mime": "^2.4.4", "schema-utils": "^1.0.0" }, "dependencies": { "mime": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", - "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", "dev": true + }, + "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" + } } } }, "url-parse": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.5.tgz", - "integrity": "sha512-4XDvC5vZRjEpjP0L4znrWeoH8P8F0XGBlfLdABi/6oV4o8xUVbTpyrxWHxkK2bT0pSIpcjdIzSoWUhlUfawCAQ==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", "dev": true, "requires": { - "querystringify": "^2.0.0", + "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, @@ -13221,18 +14217,18 @@ } }, "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", "dev": true, "requires": { - "inherits": "2.0.1" + "inherits": "2.0.3" }, "dependencies": { "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true } } @@ -13300,9 +14296,9 @@ "dev": true }, "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", + "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", "dev": true, "requires": { "spdx-correct": "^3.0.0", @@ -13338,13 +14334,10 @@ "integrity": "sha512-2ozZEFfmVvQcHWoHLNuiKlUfDKlhh4KGsy54U0UrlLMR1SO+XKAIDqBxtBwHgNrekurlJwE8A9K6L49T78ZQ9Q==" }, "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", + "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==", + "dev": true }, "w3c-hr-time": { "version": "1.0.1", @@ -13355,17 +14348,6 @@ "browser-process-hrtime": "^0.1.2" } }, - "w3c-xmlserializer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", - "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", - "dev": true, - "requires": { - "domexception": "^1.0.1", - "webidl-conversions": "^4.0.2", - "xml-name-validator": "^3.0.0" - } - }, "watchpack": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", @@ -13393,9 +14375,9 @@ "dev": true }, "webpack": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.31.0.tgz", - "integrity": "sha512-n6RVO3X0LbbipoE62akME9K/JI7qYrwwufs20VvgNNpqUoH4860KkaxJTbGq5bgkVZF9FqyyTG/0WPLH3PVNJA==", + "version": "4.35.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.35.2.tgz", + "integrity": "sha512-TZAmorNymV4q66gAM/h90cEjG+N3627Q2MnkSgKlX/z3DlNVKUtqy57lz1WmZU2+FUZwzM+qm7cGaO95PyrX5A==", "dev": true, "requires": { "@webassemblyjs/ast": "1.8.5", @@ -13424,14 +14406,21 @@ "webpack-sources": "^1.3.0" }, "dependencies": { - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "dev": true + }, + "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": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" } } } @@ -13457,6 +14446,12 @@ "ws": "^6.0.0" }, "dependencies": { + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "dev": true + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -13477,6 +14472,12 @@ "supports-color": "^5.3.0" } }, + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -13485,55 +14486,65 @@ "requires": { "has-flag": "^3.0.0" } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } } } }, "webpack-dev-middleware": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.6.2.tgz", - "integrity": "sha512-A47I5SX60IkHrMmZUlB0ZKSWi29TZTcPz7cha1Z75yYOsgWh/1AcPmQEbC8ZIbU3A1ytSv1PMU0PyPz2Lmz2jg==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.0.tgz", + "integrity": "sha512-qvDesR1QZRIAZHOE3iQ4CXLZZSQ1lAUsSpnQmlB1PBfoN/xdRjmge3Dok0W4IdaVLJOGJy3sGI4sZHwjRU0PCA==", "dev": true, "requires": { "memory-fs": "^0.4.1", - "mime": "^2.3.1", - "range-parser": "^1.0.3", + "mime": "^2.4.2", + "range-parser": "^1.2.1", "webpack-log": "^2.0.0" }, "dependencies": { "mime": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.2.tgz", - "integrity": "sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", "dev": true } } }, "webpack-dev-server": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.3.1.tgz", - "integrity": "sha512-jY09LikOyGZrxVTXK0mgIq9y2IhCoJ05848dKZqX1gAGLU1YDqgpOT71+W53JH/wI4v6ky4hm+KvSyW14JEs5A==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.7.2.tgz", + "integrity": "sha512-mjWtrKJW2T9SsjJ4/dxDC2fkFVUw8jlpemDERqV0ZJIkjjjamR2AbQlr3oz+j4JLhYCHImHnXZK5H06P2wvUew==", "dev": true, "requires": { "ansi-html": "0.0.7", "bonjour": "^3.5.0", - "chokidar": "^2.1.5", + "chokidar": "^2.1.6", "compression": "^1.7.4", "connect-history-api-fallback": "^1.6.0", "debug": "^4.1.1", - "del": "^4.1.0", - "express": "^4.16.4", + "del": "^4.1.1", + "express": "^4.17.1", "html-entities": "^1.2.1", "http-proxy-middleware": "^0.19.1", "import-local": "^2.0.0", - "internal-ip": "^4.2.0", + "internal-ip": "^4.3.0", "ip": "^1.1.5", "killable": "^1.0.1", - "loglevel": "^1.6.1", + "loglevel": "^1.6.3", "opn": "^5.5.0", + "p-retry": "^3.0.1", "portfinder": "^1.0.20", "schema-utils": "^1.0.0", "selfsigned": "^1.10.4", - "semver": "^6.0.0", + "semver": "^6.1.1", "serve-index": "^1.9.1", "sockjs": "0.3.19", "sockjs-client": "1.3.0", @@ -13541,7 +14552,7 @@ "strip-ansi": "^3.0.1", "supports-color": "^6.1.0", "url": "^0.11.0", - "webpack-dev-middleware": "^3.6.2", + "webpack-dev-middleware": "^3.7.0", "webpack-log": "^2.0.0", "yargs": "12.0.5" }, @@ -13552,6 +14563,12 @@ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, "cliui": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", @@ -13583,41 +14600,12 @@ "ms": "^2.1.1" } }, - "del": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.0.tgz", - "integrity": "sha512-C4kvKNlYrwXhKxz97BuohF8YoGgQ23Xm9lvoHmgT7JaPGprSEjk3+XFled74Yt/x0ZABUHg2D67covzAPUKx5Q==", - "dev": true, - "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - } - }, "invert-kv": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, - "is-path-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.0.0.tgz", - "integrity": "sha512-m5dHHzpOXEiv18JEORttBO64UgTEypx99vCxQLjbBvGhOJxnTNglYoFXxwo6AbsQb79sqqycQEHv2hWkHZAijA==", - "dev": true - }, - "is-path-in-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.0.0.tgz", - "integrity": "sha512-6Vz5Gc9s/sDA3JBVu0FzWufm8xaBsqy1zn8Q6gmvGP6nSDMw78aS4poBNeatWjaRpTpxxLn1WOndAiOlk+qY8A==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, "lcid": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", @@ -13628,11 +14616,20 @@ } }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, "os-locale": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", @@ -13644,16 +14641,21 @@ "mem": "^4.0.0" } }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true + "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" + } }, "semver": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", - "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.2.tgz", + "integrity": "sha512-z4PqiCpomGtWj8633oeAdXm1Kn1W++3T8epkZYnwiVgIYIJ0QHszhInYSJTYxebByQH7KVCEAn8R9duzZW2PhQ==", "dev": true }, "supports-color": { @@ -13730,12 +14732,13 @@ } }, "websocket-driver": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", + "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", "dev": true, "requires": { - "http-parser-js": ">=0.4.0", + "http-parser-js": ">=0.4.0 <0.4.11", + "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, @@ -13746,37 +14749,58 @@ "dev": true }, "websocket-stream": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.3.0.tgz", - "integrity": "sha512-L4kN0NYwZJc0q7QKVHyjS7oNx2TuLyx6P2RDVJwejuKRYklU72FwLMRmQP/1UEYwNVTJgjBtNpde4NvqeyswLQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.5.0.tgz", + "integrity": "sha512-EXy/zXb9kNHI07TIMz1oIUIrPZxQRA8aeJ5XYg5ihV8K4kD1DuA+FY6R96HfdIHzlSzS8HiISAfrm+vVQkZBug==", "dev": true, "requires": { - "duplexify": "^3.6.1", + "duplexify": "^3.5.1", "inherits": "^2.0.1", - "readable-stream": "^3.0.0", + "readable-stream": "^2.3.3", "safe-buffer": "^5.1.2", - "ws": "^6.1.2", + "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": "3.2.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.2.0.tgz", - "integrity": "sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw==", + "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": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "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": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "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" + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" } } } @@ -13791,15 +14815,15 @@ } }, "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz", + "integrity": "sha512-5YSO1nMd5D1hY3WzAQV3PzZL83W3YeyR1yW9PcH26Weh1t+Vzh9B6XkDh7aXm83HBZ4nSMvkjvN2H2ySWIvBgw==", "dev": true }, "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", "dev": true, "requires": { "lodash.sortby": "^4.7.0", @@ -13808,9 +14832,9 @@ } }, "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -13831,41 +14855,6 @@ "string-width": "^1.0.2 || 2" } }, - "winston": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", - "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", - "dev": true, - "requires": { - "async": "~1.0.0", - "colors": "1.0.x", - "cycle": "1.0.x", - "eyes": "0.1.x", - "isstream": "0.1.x", - "pkginfo": "0.3.x", - "stack-trace": "0.0.x" - }, - "dependencies": { - "async": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", - "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", - "dev": true - }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true - }, - "pkginfo": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", - "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=", - "dev": true - } - } - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -13888,18 +14877,6 @@ "requires": { "loader-utils": "^1.0.0", "schema-utils": "^0.4.0" - }, - "dependencies": { - "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - } - } } }, "wrap-ansi": { @@ -13950,9 +14927,9 @@ } }, "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", "dev": true, "requires": { "async-limiter": "~1.0.0" @@ -13987,19 +14964,15 @@ "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": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" - }, - "xmlchars": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-1.3.1.tgz", - "integrity": "sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw==", - "dev": true - }, "xmlcreate": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.1.tgz", diff --git a/package.json b/package.json index 4ae59d3c..cece0e40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.31.3", + "version": "9.0.5", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", @@ -27,31 +27,38 @@ "type": "git", "url": "https://github.com/gchq/CyberChef/" }, - "main": "build/node/CyberChef.js", + "main": "src/node/cjs.js", + "module": "src/node/index.mjs", "bugs": "https://github.com/gchq/CyberChef/issues", + "browserslist": [ + "Chrome >= 40", + "Firefox >= 35", + "Edge >= 14", + "node >= 6.5" + ], "devDependencies": { - "@babel/core": "^7.4.4", - "@babel/plugin-transform-runtime": "^7.4.4", - "@babel/preset-env": "^7.4.4", - "autoprefixer": "^9.5.1", - "babel-eslint": "^10.0.1", - "babel-loader": "^8.0.5", - "babel-plugin-syntax-dynamic-import": "^6.18.0", - "chromedriver": "^74.0.0", + "@babel/core": "^7.5.0", + "@babel/plugin-transform-runtime": "^7.5.0", + "@babel/preset-env": "^7.5.0", + "autoprefixer": "^9.6.0", + "babel-eslint": "^10.0.2", + "babel-loader": "^8.0.6", + "babel-plugin-dynamic-import-node": "^2.2.0", + "chromedriver": "^75.0.1", "colors": "^1.3.3", - "css-loader": "^2.1.1", - "eslint": "^5.16.0", + "css-loader": "^3.0.0", + "eslint": "^6.0.1", "exports-loader": "^0.7.0", - "file-loader": "^3.0.1", + "file-loader": "^4.0.0", "grunt": "^1.0.4", "grunt-accessibility": "~6.0.0", "grunt-chmod": "~1.1.1", - "grunt-concurrent": "^2.3.1", + "grunt-concurrent": "^3.0.0", "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", + "grunt-eslint": "^22.0.0", "grunt-exec": "~3.0.0", "grunt-jsdoc": "^2.4.0", "grunt-webpack": "^3.1.3", @@ -60,71 +67,73 @@ "imports-loader": "^0.8.0", "ink-docstrap": "^1.3.2", "jsdoc-babel": "^0.5.0", - "mini-css-extract-plugin": "^0.6.0", - "nightwatch": "^1.0.19", + "mini-css-extract-plugin": "^0.7.0", + "nightwatch": "^1.1.13", "node-sass": "^4.12.0", - "postcss-css-variables": "^0.12.0", + "postcss-css-variables": "^0.13.0", "postcss-import": "^12.0.1", "postcss-loader": "^3.0.0", "prompt": "^1.0.0", "sass-loader": "^7.1.0", - "sitemap": "^2.2.0", + "sitemap": "^3.2.0", "style-loader": "^0.23.1", - "svg-url-loader": "^2.3.2", - "url-loader": "^1.1.2", - "webpack": "^4.31.0", + "svg-url-loader": "^3.0.0", + "url-loader": "^2.0.1", + "webpack": "^4.35.2", "webpack-bundle-analyzer": "^3.3.2", - "webpack-dev-server": "^3.3.1", + "webpack-dev-server": "^3.7.2", "webpack-node-externals": "^1.7.2", "worker-loader": "^2.0.0" }, "dependencies": { "@babel/polyfill": "^7.4.4", - "@babel/runtime": "^7.4.4", + "@babel/runtime": "^7.5.0", "arrive": "^2.4.1", "babel-plugin-transform-builtin-extend": "1.1.2", "bcryptjs": "^2.4.3", - "bignumber.js": "^8.1.1", + "bignumber.js": "^9.0.0", "blakejs": "^1.1.0", - "bootstrap": "4.2.1", - "bootstrap-colorpicker": "^2.5.3", - "bootstrap-material-design": "^4.1.1", + "bootstrap": "4.3.1", + "bootstrap-colorpicker": "^3.1.2", + "bootstrap-material-design": "^4.1.2", "bson": "^4.0.2", "chi-squared": "^1.1.0", "clippyjs": "0.0.3", - "core-js": "^3.0.1", + "core-js": "^3.1.4", "crypto-api": "^0.8.3", "crypto-js": "^3.1.9-1", "ctph.js": "0.0.5", - "d3": "^5.9.2", + "d3": "^5.9.7", "d3-hexbin": "^0.2.2", "diff": "^4.0.1", "es6-promisify": "^6.0.1", "escodegen": "^1.11.1", + "esm": "^3.2.25", "esmangle": "^1.0.1", "esprima": "^4.0.1", "exif-parser": "^0.1.12", - "file-saver": "^2.0.1", + "file-saver": "^2.0.2", "geodesy": "^1.1.3", - "highlight.js": "^9.15.6", + "highlight.js": "^9.15.8", "jimp": "^0.6.4", "jquery": "3.4.1", "js-crc": "^0.2.0", "js-sha3": "^0.8.0", "jsesc": "^2.5.2", - "jsonpath": "^1.0.1", + "jsonpath": "^1.0.2", "jsonwebtoken": "^8.5.1", "jsqr": "^1.2.0", "jsrsasign": "8.0.12", - "kbpgp": "2.1.0", + "kbpgp": "2.1.2", + "libbzip2-wasm": "0.0.4", "libyara-wasm": "0.0.12", - "lodash": "^4.17.11", - "loglevel": "^1.6.1", + "lodash": "^4.17.15", + "loglevel": "^1.6.3", "loglevel-message-prefix": "^3.0.0", "moment": "^2.24.0", "moment-timezone": "^0.5.25", "ngeohash": "^0.6.3", - "node-forge": "^0.8.2", + "node-forge": "^0.8.5", "node-md6": "^0.1.0", "nodom": "^2.2.0", "notepack.io": "^2.2.0", @@ -132,12 +141,12 @@ "otp": "^0.1.3", "popper.js": "^1.15.0", "qr-image": "^3.2.0", - "scryptsy": "^2.0.0", + "scryptsy": "^2.1.0", "snackbarjs": "^1.1.0", "sortablejs": "^1.9.0", - "split.js": "^1.5.10", + "split.js": "^1.5.11", "ssdeep.js": "0.0.2", - "ua-parser-js": "^0.7.19", + "ua-parser-js": "^0.7.20", "utf8": "^3.0.0", "vkbeautify": "^0.99.3", "xmldom": "^0.1.27", @@ -148,7 +157,9 @@ "scripts": { "start": "grunt dev", "build": "grunt prod", + "repl": "node src/node/repl.js", "test": "grunt test", + "test-node-consumer": "grunt testnodeconsumer", "testui": "grunt testui", "docs": "grunt docs", "lint": "grunt lint", diff --git a/postcss.config.js b/postcss.config.js index 55ea22be..2d46543d 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,13 +1,7 @@ module.exports = { plugins: [ require("postcss-import"), - require("autoprefixer")({ - browsers: [ - "Chrome >= 40", - "Firefox >= 35", - "Edge >= 14" - ] - }), + require("autoprefixer"), require("postcss-css-variables")({ preserve: true }), diff --git a/src/core/Chef.mjs b/src/core/Chef.mjs index 33b7acbe..ca02a32d 100755 --- a/src/core/Chef.mjs +++ b/src/core/Chef.mjs @@ -4,9 +4,10 @@ * @license Apache-2.0 */ -import Dish from "./Dish"; -import Recipe from "./Recipe"; +import Dish from "./Dish.mjs"; +import Recipe from "./Recipe.mjs"; import log from "loglevel"; +import { isWorkerEnvironment } from "./Utils.mjs"; /** * The main controller for CyberChef. @@ -28,8 +29,6 @@ class Chef { * @param {Object[]} recipeConfig - The recipe configuration object * @param {Object} options - The options object storing various user choices * @param {boolean} options.attempHighlight - Whether or not to attempt highlighting - * @param {number} progress - The position in the recipe to start from - * @param {number} [step] - Whether to only execute one operation in the recipe * * @returns {Object} response * @returns {string} response.result - The output of the recipe @@ -38,46 +37,20 @@ class Chef { * @returns {number} response.duration - The number of ms it took to execute the recipe * @returns {number} response.error - The error object thrown by a failed operation (false if no error) */ - async bake(input, recipeConfig, options, progress, step) { + async bake(input, recipeConfig, options) { log.debug("Chef baking"); const startTime = new Date().getTime(), recipe = new Recipe(recipeConfig), containsFc = recipe.containsFlowControl(), - notUTF8 = options && options.hasOwnProperty("treatAsUtf8") && !options.treatAsUtf8; - let error = false; - - if (containsFc && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false); - - // Clean up progress - if (progress >= recipeConfig.length) { + notUTF8 = options && "treatAsUtf8" in options && !options.treatAsUtf8; + let error = false, progress = 0; - } - if (step) { - // Unset breakpoint on this step - recipe.setBreakpoint(progress, false); - // Set breakpoint on next step - recipe.setBreakpoint(progress + 1, true); - } + if (containsFc && isWorkerEnvironment()) self.setOption("attemptHighlight", false); - // If the previously run operation presented a different value to its - // normal output, we need to recalculate it. - if (recipe.lastOpPresented(progress)) { - progress = 0; - } - - // If stepping with flow control, we have to start from the beginning - // but still want to skip all previous breakpoints - if (progress > 0 && containsFc) { - recipe.removeBreaksUpTo(progress); - progress = 0; - } - - // If starting from scratch, load data - if (progress === 0) { - const type = input instanceof ArrayBuffer ? Dish.ARRAY_BUFFER : Dish.STRING; - this.dish.set(input, type); - } + // Load data + const type = input instanceof ArrayBuffer ? Dish.ARRAY_BUFFER : Dish.STRING; + this.dish.set(input, type); try { progress = await recipe.execute(this.dish, progress); @@ -196,6 +169,18 @@ class Chef { return await newDish.get(type); } + /** + * Gets the title of a dish and returns it + * + * @param {Dish} dish + * @param {number} [maxLength=100] + * @returns {string} + */ + async getDishTitle(dish, maxLength=100) { + const newDish = new Dish(dish); + return await newDish.getTitle(maxLength); + } + } export default Chef; diff --git a/src/core/ChefWorker.js b/src/core/ChefWorker.js index ad498936..b0ba31a0 100644 --- a/src/core/ChefWorker.js +++ b/src/core/ChefWorker.js @@ -6,9 +6,9 @@ * @license Apache-2.0 */ -import Chef from "./Chef"; +import Chef from "./Chef.mjs"; import OperationConfig from "./config/OperationConfig.json"; -import OpModules from "./config/modules/OpModules"; +import OpModules from "./config/modules/OpModules.mjs"; // Add ">" to the start of all log messages in the Chef Worker import loglevelMessagePrefix from "loglevel-message-prefix"; @@ -25,6 +25,8 @@ self.chef = new Chef(); self.OpModules = OpModules; self.OperationConfig = OperationConfig; +self.inputNum = -1; + // Tell the app that the worker has loaded and is ready to operate self.postMessage({ @@ -35,6 +37,9 @@ self.postMessage({ /** * Respond to message from parent thread. * + * inputNum is optional and only used for baking multiple inputs. + * Defaults to -1 when one isn't sent with the bake message. + * * Messages should have the following format: * { * action: "bake" | "silentBake", @@ -43,8 +48,9 @@ self.postMessage({ * recipeConfig: {[Object]}, * options: {Object}, * progress: {number}, - * step: {boolean} - * } | undefined + * step: {boolean}, + * [inputNum=-1]: {number} + * } * } */ self.addEventListener("message", function(e) { @@ -62,6 +68,9 @@ self.addEventListener("message", function(e) { case "getDishAs": getDishAs(r.data); break; + case "getDishTitle": + getDishTitle(r.data); + break; case "docURL": // Used to set the URL of the current document so that scripts can be // imported into an inline worker. @@ -91,30 +100,35 @@ self.addEventListener("message", function(e) { async function bake(data) { // Ensure the relevant modules are loaded self.loadRequiredModules(data.recipeConfig); - try { + self.inputNum = (data.inputNum !== undefined) ? data.inputNum : -1; const response = await self.chef.bake( data.input, // The user's input data.recipeConfig, // The configuration of the recipe - data.options, // Options set by the user - data.progress, // The current position in the recipe - data.step // Whether or not to take one step or execute the whole recipe + data.options // Options set by the user ); + const transferable = (data.input instanceof ArrayBuffer) ? [data.input] : undefined; self.postMessage({ action: "bakeComplete", data: Object.assign(response, { - id: data.id + id: data.id, + inputNum: data.inputNum, + bakeId: data.bakeId }) - }); + }, transferable); + } catch (err) { self.postMessage({ action: "bakeError", - data: Object.assign(err, { - id: data.id - }) + data: { + error: err.message || err, + id: data.id, + inputNum: data.inputNum + } }); } + self.inputNum = -1; } @@ -136,13 +150,33 @@ function silentBake(data) { */ async function getDishAs(data) { const value = await self.chef.getDishAs(data.dish, data.type); - + const transferable = (data.type === "ArrayBuffer") ? [value] : undefined; self.postMessage({ action: "dishReturned", data: { value: value, id: data.id } + }, transferable); +} + + +/** + * Gets the dish title + * + * @param {object} data + * @param {Dish} data.dish + * @param {number} data.maxLength + * @param {number} data.id + */ +async function getDishTitle(data) { + const title = await self.chef.getDishTitle(data.dish, data.maxLength); + self.postMessage({ + action: "dishReturned", + data: { + value: title, + id: data.id + } }); } @@ -175,7 +209,7 @@ self.loadRequiredModules = function(recipeConfig) { recipeConfig.forEach(op => { const module = self.OperationConfig[op.op].module; - if (!OpModules.hasOwnProperty(module)) { + if (!(module in OpModules)) { log.info(`Loading ${module} module`); self.sendStatusMessage(`Loading ${module} module`); self.importScripts(`${self.docURL}/modules/${module}.js`); @@ -193,7 +227,28 @@ self.loadRequiredModules = function(recipeConfig) { self.sendStatusMessage = function(msg) { self.postMessage({ action: "statusMessage", - data: msg + data: { + message: msg, + inputNum: self.inputNum + } + }); +}; + + +/** + * Send progress update to the app. + * + * @param {number} progress + * @param {number} total + */ +self.sendProgressMessage = function(progress, total) { + self.postMessage({ + action: "progressMessage", + data: { + progress: progress, + total: total, + inputNum: self.inputNum + } }); }; diff --git a/src/core/Dish.mjs b/src/core/Dish.mjs index 96aea716..9c878cf6 100755 --- a/src/core/Dish.mjs +++ b/src/core/Dish.mjs @@ -5,11 +5,22 @@ * @license Apache-2.0 */ -import Utils from "./Utils"; -import DishError from "./errors/DishError"; +import Utils, { isNodeEnvironment } from "./Utils.mjs"; +import DishError from "./errors/DishError.mjs"; import BigNumber from "bignumber.js"; +import { detectFileType } from "./lib/FileType.mjs"; import log from "loglevel"; +import DishByteArray from "./dishTypes/DishByteArray.mjs"; +import DishBigNumber from "./dishTypes/DishBigNumber.mjs"; +import DishFile from "./dishTypes/DishFile.mjs"; +import DishHTML from "./dishTypes/DishHTML.mjs"; +import DishJSON from "./dishTypes/DishJSON.mjs"; +import DishListFile from "./dishTypes/DishListFile.mjs"; +import DishNumber from "./dishTypes/DishNumber.mjs"; +import DishString from "./dishTypes/DishString.mjs"; + + /** * The data being operated on by each operation. */ @@ -18,16 +29,27 @@ class Dish { /** * Dish constructor * - * @param {Dish} [dish=null] - A dish to clone + * @param {Dish || *} [dishOrInput=null] - A dish to clone OR an object + * literal to make into a dish + * @param {Enum} [type=null] (optional) - A type to accompany object + * literal input */ - constructor(dish=null) { + constructor(dishOrInput=null, type = null) { this.value = new ArrayBuffer(0); this.type = Dish.ARRAY_BUFFER; - if (dish && - dish.hasOwnProperty("value") && - dish.hasOwnProperty("type")) { - this.set(dish.value, dish.type); + // Case: dishOrInput is dish object + if (dishOrInput && + Object.prototype.hasOwnProperty.call(dishOrInput, "value") && + Object.prototype.hasOwnProperty.call(dishOrInput, "type")) { + this.set(dishOrInput.value, dishOrInput.type); + // input and type defined separately + } else if (dishOrInput && type !== null) { + this.set(dishOrInput, type); + // No type declared, so infer it. + } else if (dishOrInput) { + const inferredType = Dish.typeEnum(dishOrInput.constructor.name); + this.set(dishOrInput, inferredType); } } @@ -56,6 +78,7 @@ class Dish { case "big number": return Dish.BIG_NUMBER; case "json": + case "object": // object constructor name. To allow JSON input in node. return Dish.JSON; case "file": return Dish.FILE; @@ -99,6 +122,43 @@ class Dish { } + /** + * Returns the value of the data in the type format specified. + * + * If running in a browser, get is asynchronous. + * + * @param {number} type - The data type of value, see Dish enums. + * @param {boolean} [notUTF8=false] - Do not treat strings as UTF8. + * @returns {* | Promise} - (Broswer) A promise | (Node) value of dish in given type + */ + get(type, notUTF8=false) { + if (typeof type === "string") { + type = Dish.typeEnum(type); + } + + if (this.type !== type) { + + // Node environment => _translate is sync + if (isNodeEnvironment()) { + this._translate(type, notUTF8); + return this.value; + + // Browser environment => _translate is async + } else { + return new Promise((resolve, reject) => { + this._translate(type, notUTF8) + .then(() => { + resolve(this.value); + }) + .catch(reject); + }); + } + } + + return this.value; + } + + /** * Sets the data value and type and then validates them. * @@ -122,116 +182,81 @@ class Dish { } } - /** - * Returns the value of the data in the type format specified. + * Returns the Dish as the given type, without mutating the original dish. + * + * If running in a browser, get is asynchronous. + * + * @Node * * @param {number} type - The data type of value, see Dish enums. * @param {boolean} [notUTF8=false] - Do not treat strings as UTF8. - * @returns {*} - The value of the output data. + * @returns {Dish | Promise} - (Broswer) A promise | (Node) value of dish in given type */ - async get(type, notUTF8=false) { - if (typeof type === "string") { - type = Dish.typeEnum(type); - } - if (this.type !== type) { - await this._translate(type, notUTF8); - } - return this.value; + presentAs(type, notUTF8=false) { + const clone = this.clone(); + return clone.get(type, notUTF8); } /** - * Translates the data to the given type format. - * - * @param {number} toType - The data type of value, see Dish enums. - * @param {boolean} [notUTF8=false] - Do not treat strings as UTF8. + * Detects the MIME type of the current dish + * @returns {string} */ - async _translate(toType, notUTF8=false) { - log.debug(`Translating Dish from ${Dish.enumLookup(this.type)} to ${Dish.enumLookup(toType)}`); + detectDishType() { + const data = new Uint8Array(this.value.slice(0, 2048)), + types = detectFileType(data); - // Convert data to intermediate ArrayBuffer type - try { - switch (this.type) { - case Dish.STRING: - this.value = this.value ? Utils.strToArrayBuffer(this.value) : new ArrayBuffer; - break; - case Dish.NUMBER: - this.value = typeof this.value === "number" ? Utils.strToArrayBuffer(this.value.toString()) : new ArrayBuffer; - break; - case Dish.HTML: - this.value = this.value ? Utils.strToArrayBuffer(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : new ArrayBuffer; - break; - case Dish.BYTE_ARRAY: - this.value = new Uint8Array(this.value).buffer; - break; - case Dish.BIG_NUMBER: - this.value = BigNumber.isBigNumber(this.value) ? Utils.strToArrayBuffer(this.value.toFixed()) : new ArrayBuffer; - break; - case Dish.JSON: - this.value = this.value ? Utils.strToArrayBuffer(JSON.stringify(this.value, null, 4)) : new ArrayBuffer; - break; - case Dish.FILE: - this.value = (await Utils.readFile(this.value)).buffer; - break; - case Dish.LIST_FILE: - this.value = await Promise.all(this.value.map(async f => Utils.readFile(f))); - this.value = concatenateTypedArrays(...this.value).buffer; - break; - default: - break; - } - } catch (err) { - throw new DishError(`Error translating from ${Dish.enumLookup(this.type)} to ArrayBuffer: ${err}`); - } - - this.type = Dish.ARRAY_BUFFER; - - // Convert from ArrayBuffer to toType - try { - switch (toType) { - case Dish.STRING: - case Dish.HTML: - this.value = this.value ? Utils.arrayBufferToStr(this.value, !notUTF8) : ""; - this.type = Dish.STRING; - break; - case Dish.NUMBER: - this.value = this.value ? parseFloat(Utils.arrayBufferToStr(this.value, !notUTF8)) : 0; - this.type = Dish.NUMBER; - break; - case Dish.BYTE_ARRAY: - this.value = Array.prototype.slice.call(new Uint8Array(this.value)); - this.type = Dish.ARRAY_BUFFER; - break; - case Dish.BIG_NUMBER: - try { - this.value = new BigNumber(Utils.arrayBufferToStr(this.value, !notUTF8)); - } catch (err) { - this.value = new BigNumber(NaN); - } - this.type = Dish.BIG_NUMBER; - break; - case Dish.JSON: - this.value = JSON.parse(Utils.arrayBufferToStr(this.value, !notUTF8)); - this.type = Dish.JSON; - break; - case Dish.FILE: - this.value = new File(this.value, "unknown"); - this.type = Dish.FILE; - break; - case Dish.LIST_FILE: - this.value = [new File(this.value, "unknown")]; - this.type = Dish.LIST_FILE; - break; - default: - break; - } - } catch (err) { - throw new DishError(`Error translating from ArrayBuffer to ${Dish.enumLookup(toType)}: ${err}`); + if (!types.length || !types[0].mime || !types[0].mime === "text/plain") { + return null; + } else { + return types[0].mime; } } + /** + * Returns the title of the data up to the specified length + * + * @param {number} maxLength - The maximum title length + * @returns {string} + */ + async getTitle(maxLength) { + let title = ""; + let cloned; + + switch (this.type) { + case Dish.FILE: + title = this.value.name; + break; + case Dish.LIST_FILE: + title = `${this.value.length} file(s)`; + break; + case Dish.JSON: + title = "application/json"; + break; + case Dish.NUMBER: + case Dish.BIG_NUMBER: + title = this.value.toString(); + break; + case Dish.ARRAY_BUFFER: + case Dish.BYTE_ARRAY: + title = this.detectDishType(); + if (title !== null) break; + // fall through if no mime type was detected + default: + try { + cloned = this.clone(); + cloned.value = cloned.value.slice(0, 256); + title = await cloned.get(Dish.STRING); + } catch (err) { + log.error(`${Dish.enumLookup(this.type)} cannot be sliced. ${err}`); + } + } + + return title.slice(0, maxLength); + } + /** * Validates that the value is the type that has been specified. * May have to disable parts of BYTE_ARRAY validation if it effects performance. @@ -241,7 +266,7 @@ class Dish { valid() { switch (this.type) { case Dish.BYTE_ARRAY: - if (!(this.value instanceof Array)) { + if (!(this.value instanceof Uint8Array) && !(this.value instanceof Array)) { return false; } @@ -262,7 +287,21 @@ class Dish { case Dish.ARRAY_BUFFER: return this.value instanceof ArrayBuffer; case Dish.BIG_NUMBER: - return BigNumber.isBigNumber(this.value); + if (BigNumber.isBigNumber(this.value)) return true; + /* + If a BigNumber is passed between WebWorkers it is serialised as a JSON + object with a coefficient (c), exponent (e) and sign (s). We detect this + and reinitialise it as a BigNumber object. + */ + if (Object.keys(this.value).sort().equals(["c", "e", "s"])) { + const temp = new BigNumber(); + temp.c = this.value.c; + temp.e = this.value.e; + temp.s = this.value.s; + this.value = temp; + return true; + } + return false; case Dish.JSON: // All values can be serialised in some manner, so we return true in all cases return true; @@ -369,26 +408,109 @@ class Dish { return newDish; } -} + /** + * Translates the data to the given type format. + * + * If running in the browser, _translate is asynchronous. + * + * @param {number} toType - The data type of value, see Dish enums. + * @param {boolean} [notUTF8=false] - Do not treat strings as UTF8. + * @returns {Promise || undefined} + */ + _translate(toType, notUTF8=false) { + log.debug(`Translating Dish from ${Dish.enumLookup(this.type)} to ${Dish.enumLookup(toType)}`); + + // Node environment => translate is sync + if (isNodeEnvironment()) { + this._toArrayBuffer(); + this.type = Dish.ARRAY_BUFFER; + this._fromArrayBuffer(toType, notUTF8); + + // Browser environment => translate is async + } else { + return new Promise((resolve, reject) => { + this._toArrayBuffer() + .then(() => this.type = Dish.ARRAY_BUFFER) + .then(() => { + this._fromArrayBuffer(toType); + resolve(); + }) + .catch(reject); + }); + } -/** - * Concatenates a list of Uint8Arrays together - * - * @param {Uint8Array[]} arrays - * @returns {Uint8Array} - */ -function concatenateTypedArrays(...arrays) { - let totalLength = 0; - for (const arr of arrays) { - totalLength += arr.length; } - const result = new Uint8Array(totalLength); - let offset = 0; - for (const arr of arrays) { - result.set(arr, offset); - offset += arr.length; + + /** + * Convert this.value to an ArrayBuffer + * + * If running in a browser, _toByteArray is asynchronous. + * + * @returns {Promise || undefined} + */ + _toArrayBuffer() { + // Using 'bind' here to allow this.value to be mutated within translation functions + const toByteArrayFuncs = { + browser: { + [Dish.STRING]: () => Promise.resolve(DishString.toArrayBuffer.bind(this)()), + [Dish.NUMBER]: () => Promise.resolve(DishNumber.toArrayBuffer.bind(this)()), + [Dish.HTML]: () => Promise.resolve(DishHTML.toArrayBuffer.bind(this)()), + [Dish.ARRAY_BUFFER]: () => Promise.resolve(), + [Dish.BIG_NUMBER]: () => Promise.resolve(DishBigNumber.toArrayBuffer.bind(this)()), + [Dish.JSON]: () => Promise.resolve(DishJSON.toArrayBuffer.bind(this)()), + [Dish.FILE]: () => DishFile.toArrayBuffer.bind(this)(), + [Dish.LIST_FILE]: () => Promise.resolve(DishListFile.toArrayBuffer.bind(this)()), + [Dish.BYTE_ARRAY]: () => Promise.resolve(DishByteArray.toArrayBuffer.bind(this)()), + }, + node: { + [Dish.STRING]: () => DishString.toArrayBuffer.bind(this)(), + [Dish.NUMBER]: () => DishNumber.toArrayBuffer.bind(this)(), + [Dish.HTML]: () => DishHTML.toArrayBuffer.bind(this)(), + [Dish.ARRAY_BUFFER]: () => {}, + [Dish.BIG_NUMBER]: () => DishBigNumber.toArrayBuffer.bind(this)(), + [Dish.JSON]: () => DishJSON.toArrayBuffer.bind(this)(), + [Dish.FILE]: () => DishFile.toArrayBuffer.bind(this)(), + [Dish.LIST_FILE]: () => DishListFile.toArrayBuffer.bind(this)(), + [Dish.BYTE_ARRAY]: () => DishByteArray.toArrayBuffer.bind(this)(), + } + }; + + try { + return toByteArrayFuncs[isNodeEnvironment() && "node" || "browser"][this.type](); + } catch (err) { + throw new DishError(`Error translating from ${Dish.enumLookup(this.type)} to ArrayBuffer: ${err}`); + } } - return result; + + /** + * Convert this.value to the given type from ArrayBuffer + * + * @param {number} toType - the Dish enum to convert to + * @param {boolean} [notUTF8=false] - Do not treat strings as UTF8. + */ + _fromArrayBuffer(toType, notUTF8) { + + // Using 'bind' here to allow this.value to be mutated within translation functions + const toTypeFunctions = { + [Dish.STRING]: () => DishString.fromArrayBuffer.bind(this)(notUTF8), + [Dish.NUMBER]: () => DishNumber.fromArrayBuffer.bind(this)(notUTF8), + [Dish.HTML]: () => DishHTML.fromArrayBuffer.bind(this)(notUTF8), + [Dish.ARRAY_BUFFER]: () => {}, + [Dish.BIG_NUMBER]: () => DishBigNumber.fromArrayBuffer.bind(this)(notUTF8), + [Dish.JSON]: () => DishJSON.fromArrayBuffer.bind(this)(notUTF8), + [Dish.FILE]: () => DishFile.fromArrayBuffer.bind(this)(), + [Dish.LIST_FILE]: () => DishListFile.fromArrayBuffer.bind(this)(), + [Dish.BYTE_ARRAY]: () => DishByteArray.fromArrayBuffer.bind(this)(), + }; + + try { + toTypeFunctions[toType](); + this.type = toType; + } catch (err) { + throw new DishError(`Error translating from ArrayBuffer to ${Dish.enumLookup(toType)}: ${err}`); + } + } + } diff --git a/src/core/Ingredient.mjs b/src/core/Ingredient.mjs index 2c7154d9..f74b42d6 100755 --- a/src/core/Ingredient.mjs +++ b/src/core/Ingredient.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Utils from "./Utils"; -import {fromHex} from "./lib/Hex"; +import Utils from "./Utils.mjs"; +import {fromHex} from "./lib/Hex.mjs"; /** * The arguments to operations. diff --git a/src/core/Operation.mjs b/src/core/Operation.mjs index c0656151..32ecff07 100755 --- a/src/core/Operation.mjs +++ b/src/core/Operation.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Dish from "./Dish"; -import Ingredient from "./Ingredient"; +import Dish from "./Dish.mjs"; +import Ingredient from "./Ingredient.mjs"; /** * The Operation specified by the user to be run. diff --git a/src/core/Recipe.mjs b/src/core/Recipe.mjs index a11b4d02..14cb39b7 100755 --- a/src/core/Recipe.mjs +++ b/src/core/Recipe.mjs @@ -5,10 +5,11 @@ */ import OperationConfig from "./config/OperationConfig.json"; -import OperationError from "./errors/OperationError"; -import Operation from "./Operation"; -import DishError from "./errors/DishError"; +import OperationError from "./errors/OperationError.mjs"; +import Operation from "./Operation.mjs"; +import DishError from "./errors/DishError.mjs"; import log from "loglevel"; +import { isWorkerEnvironment } from "./Utils.mjs"; // Cache container for modules let modules = null; @@ -61,7 +62,7 @@ class Recipe { if (!modules) { // Using Webpack Magic Comments to force the dynamic import to be included in the main chunk // https://webpack.js.org/api/module-methods/ - modules = await import(/* webpackMode: "eager" */ "./config/modules/OpModules"); + modules = await import(/* webpackMode: "eager" */ "./config/modules/OpModules.mjs"); modules = modules.default; } @@ -200,7 +201,12 @@ class Recipe { try { input = await dish.get(op.inputType); - log.debug("Executing operation"); + log.debug(`Executing operation '${op.name}'`); + + if (isWorkerEnvironment()) { + self.sendStatusMessage(`Baking... (${i+1}/${this.opList.length})`); + self.sendProgressMessage(i + 1, this.opList.length); + } if (op.flowControl) { // Package up the current state diff --git a/src/core/Utils.mjs b/src/core/Utils.mjs index e4411fe0..0ea88c87 100755 --- a/src/core/Utils.mjs +++ b/src/core/Utils.mjs @@ -5,11 +5,10 @@ */ import utf8 from "utf8"; -import {fromBase64, toBase64} from "./lib/Base64"; -import {fromHex} from "./lib/Hex"; -import {fromDecimal} from "./lib/Decimal"; -import {fromBinary} from "./lib/Binary"; - +import {fromBase64, toBase64} from "./lib/Base64.mjs"; +import {fromHex} from "./lib/Hex.mjs"; +import {fromDecimal} from "./lib/Decimal.mjs"; +import {fromBinary} from "./lib/Binary.mjs"; /** * Utility functions for use in operations, the core framework and the stage. @@ -95,7 +94,7 @@ class Utils { const paddedBytes = new Array(numBytes); paddedBytes.fill(padByte); - Array.prototype.map.call(arr, function(b, i) { + [...arr].forEach((b, i) => { paddedBytes[i] = b; }); @@ -174,10 +173,11 @@ class Utils { * @returns {string} */ static printable(str, preserveWs=false) { - if (ENVIRONMENT_IS_WEB() && window.app && !window.app.options.treatAsUtf8) { + if (isWebEnvironment() && window.app && !window.app.options.treatAsUtf8) { str = Utils.byteArrayToChars(Utils.strToByteArray(str)); } + // eslint-disable-next-line no-misleading-character-class const re = /[\0-\x08\x0B-\x0C\x0E-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BB-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uD7FF\uE000-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF]/g; const wsRe = /[\x09-\x10\x0D\u2028\u2029]/g; @@ -201,11 +201,20 @@ class Utils { * Utils.parseEscapedChars("\\n"); */ static parseEscapedChars(str) { - return str.replace(/(\\)?\\([bfnrtv0'"]|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\})/g, function(m, a, b) { + return str.replace(/(\\)?\\([bfnrtv'"]|[0-3][0-7]{2}|[0-7]{1,2}|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\}|\\)/g, function(m, a, b) { if (a === "\\") return "\\"+b; switch (b[0]) { + case "\\": + return "\\"; case "0": - return "\0"; + case "1": + case "2": + case "3": + case "4": + case "5": + case "6": + case "7": + return String.fromCharCode(parseInt(b, 8)); case "b": return "\b"; case "t": @@ -411,9 +420,9 @@ class Utils { const utf8Str = utf8.encode(str); if (str.length !== utf8Str.length) { - if (ENVIRONMENT_IS_WORKER()) { + if (isWorkerEnvironment()) { self.setOption("attemptHighlight", false); - } else if (ENVIRONMENT_IS_WEB()) { + } else if (isWebEnvironment()) { window.app.options.attemptHighlight = false; } } @@ -466,9 +475,9 @@ class Utils { const utf8Str = utf8.encode(str); if (str.length !== utf8Str.length) { - if (ENVIRONMENT_IS_WORKER()) { + if (isWorkerEnvironment()) { self.setOption("attemptHighlight", false); - } else if (ENVIRONMENT_IS_WEB()) { + } else if (isWebEnvironment()) { window.app.options.attemptHighlight = false; } } @@ -528,11 +537,10 @@ class Utils { const str = Utils.byteArrayToChars(byteArray); try { const utf8Str = utf8.decode(str); - if (str.length !== utf8Str.length) { - if (ENVIRONMENT_IS_WORKER()) { + if (isWorkerEnvironment()) { self.setOption("attemptHighlight", false); - } else if (ENVIRONMENT_IS_WEB()) { + } else if (isWebEnvironment()) { window.app.options.attemptHighlight = false; } } @@ -985,7 +993,7 @@ class Utils { /** * Reads a File and returns the data as a Uint8Array. * - * @param {File} file + * @param {File | for node: array|arrayBuffer|buffer|string} file * @returns {Uint8Array} * * @example @@ -993,33 +1001,57 @@ class Utils { * await Utils.readFile(new File(["hello"], "test")) */ static readFile(file) { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - const data = new Uint8Array(file.size); - let offset = 0; - const CHUNK_SIZE = 10485760; // 10MiB - const seek = function() { - if (offset >= file.size) { - resolve(data); - return; - } - const slice = file.slice(offset, offset + CHUNK_SIZE); - reader.readAsArrayBuffer(slice); - }; + if (isNodeEnvironment()) { + return Buffer.from(file).buffer; + + } else { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + const data = new Uint8Array(file.size); + let offset = 0; + const CHUNK_SIZE = 10485760; // 10MiB + + const seek = function() { + if (offset >= file.size) { + resolve(data); + return; + } + const slice = file.slice(offset, offset + CHUNK_SIZE); + reader.readAsArrayBuffer(slice); + }; + + reader.onload = function(e) { + data.set(new Uint8Array(reader.result), offset); + offset += CHUNK_SIZE; + seek(); + }; + + reader.onerror = function(e) { + reject(reader.error.message); + }; - reader.onload = function(e) { - data.set(new Uint8Array(reader.result), offset); - offset += CHUNK_SIZE; seek(); - }; + }); + } + } - reader.onerror = function(e) { - reject(reader.error.message); - }; + /** + * Synchronously read the raw data from a File object. + * + * Only works in the Node environment + * + * @param {File} file - a File shim object (see src/node/File.mjs) + * @returns {ArrayBuffer} the data from the file in an ArrayBuffer + * @throws {TypeError} thrown if the method is called from a browser environment + */ + static readFileSync(file) { + if (!isNodeEnvironment()) { + throw new TypeError("Browser environment cannot support readFileSync"); + } - seek(); - }); + const arrayBuffer = Uint8Array.from(file.data); + return arrayBuffer.buffer; } @@ -1121,6 +1153,30 @@ class Utils { } +/** + * Check whether the code is running in a Node.js environment + * @returns {boolean} + */ +export function isNodeEnvironment() { + return typeof process !== "undefined" && process.versions != null && process.versions.node != null; +} + +/** + * Check whether the code is running in a web environment + * @returns {boolean} +*/ +export function isWebEnvironment() { + return typeof window === "object"; +} + +/** + * Check whether the code is running in a worker + * @returns {boolean} +*/ +export function isWorkerEnvironment() { + return typeof importScripts === "function"; +} + export default Utils; @@ -1139,7 +1195,7 @@ export default Utils; Array.prototype.unique = function() { const u = {}, a = []; for (let i = 0, l = this.length; i < l; i++) { - if (u.hasOwnProperty(this[i])) { + if (Object.prototype.hasOwnProperty.call(u, this[i])) { continue; } a.push(this[i]); @@ -1238,12 +1294,13 @@ String.prototype.count = function(chr) { * @param {string} msg */ export function sendStatusMessage(msg) { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage(msg); - else if (ENVIRONMENT_IS_WEB()) + else if (isWebEnvironment()) app.alert(msg, 10000); - else if (ENVIRONMENT_IS_NODE()) - log.debug(msg); + else if (isNodeEnvironment()) + // eslint-disable-next-line no-console + console.debug(msg); } diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index ae5c68ed..f590b02c 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -198,8 +198,8 @@ "Remove null bytes", "To Upper case", "To Lower case", - "To Case Insensitive Regex", - "From Case Insensitive Regex", + "To Case Insensitive Regex", + "From Case Insensitive Regex", "Add line numbers", "Remove line numbers", "To Table", @@ -303,6 +303,8 @@ "Snefru", "BLAKE2b", "BLAKE2s", + "GOST hash", + "Streebog", "SSDEEP", "CTPH", "Compare SSDEEP hashes", @@ -317,6 +319,7 @@ "Fletcher-32 Checksum", "Fletcher-64 Checksum", "Adler-32 Checksum", + "CRC-8 Checksum", "CRC-16 Checksum", "CRC-32 Checksum", "TCP/IP Checksum" @@ -385,6 +388,9 @@ "Contain Image", "Cover Image", "Image Hue/Saturation/Lightness", + "Sharpen Image", + "Convert Image Format", + "Add Text To Image", "Hex Density chart", "Scatter chart", "Series chart", @@ -396,6 +402,7 @@ "ops": [ "Entropy", "Frequency distribution", + "Index of Coincidence", "Chi Square", "Disassemble x86", "Pseudo-Random Number Generator", diff --git a/src/core/config/scripts/generateConfig.mjs b/src/core/config/scripts/generateConfig.mjs index 335d47b8..8ef906e6 100644 --- a/src/core/config/scripts/generateConfig.mjs +++ b/src/core/config/scripts/generateConfig.mjs @@ -14,7 +14,7 @@ import path from "path"; import fs from "fs"; import process from "process"; -import * as Ops from "../../operations/index"; +import * as Ops from "../../operations/index.mjs"; const dir = path.join(process.cwd() + "/src/core/config/"); if (!fs.existsSync(dir)) { @@ -45,11 +45,11 @@ for (const opObj in Ops) { args: op.args }; - if (op.hasOwnProperty("patterns")) { + if ("patterns" in op) { operationConfig[op.name].patterns = op.patterns; } - if (!modules.hasOwnProperty(op.module)) + if (!(op.module in modules)) modules[op.module] = {}; modules[op.module][op.name] = opObj; } @@ -84,7 +84,7 @@ for (const module in modules) { for (const opName in modules[module]) { const objName = modules[module][opName]; - code += `import ${objName} from "../../operations/${objName}";\n`; + code += `import ${objName} from "../../operations/${objName}.mjs";\n`; } code += ` @@ -124,7 +124,7 @@ let opModulesCode = `/** `; for (const module in modules) { - opModulesCode += `import ${module}Module from "./${module}";\n`; + opModulesCode += `import ${module}Module from "./${module}.mjs";\n`; } opModulesCode += ` diff --git a/src/core/config/scripts/generateOpsIndex.mjs b/src/core/config/scripts/generateOpsIndex.mjs index 49cd635c..d18307bc 100644 --- a/src/core/config/scripts/generateOpsIndex.mjs +++ b/src/core/config/scripts/generateOpsIndex.mjs @@ -39,7 +39,7 @@ let code = `/** `; opObjs.forEach(obj => { - code += `import ${obj} from "./${obj}";\n`; + code += `import ${obj} from "./${obj}.mjs";\n`; }); code += ` diff --git a/src/core/config/scripts/newOperation.mjs b/src/core/config/scripts/newOperation.mjs index ac25b383..03753fae 100644 --- a/src/core/config/scripts/newOperation.mjs +++ b/src/core/config/scripts/newOperation.mjs @@ -13,7 +13,7 @@ import colors from "colors"; import process from "process"; import fs from "fs"; import path from "path"; -import EscapeString from "../../operations/EscapeString"; +import EscapeString from "../../operations/EscapeString.mjs"; const dir = path.join(process.cwd() + "/src/core/operations/"); @@ -130,8 +130,8 @@ prompt.get(schema, (err, result) => { * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * ${result.opName} operation diff --git a/src/core/dishTypes/DishBigNumber.mjs b/src/core/dishTypes/DishBigNumber.mjs new file mode 100644 index 00000000..d6f67698 --- /dev/null +++ b/src/core/dishTypes/DishBigNumber.mjs @@ -0,0 +1,39 @@ +/** + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import DishType from "./DishType.mjs"; +import Utils from "../Utils.mjs"; +import BigNumber from "bignumber.js"; + +/** + * translation methods for BigNumber Dishes + */ +class DishBigNumber extends DishType { + + /** + * convert the given value to a ArrayBuffer + * @param {BigNumber} value + */ + static toArrayBuffer() { + DishBigNumber.checkForValue(this.value); + this.value = BigNumber.isBigNumber(this.value) ? Utils.strToArrayBuffer(this.value.toFixed()) : new ArrayBuffer; + } + + /** + * convert the given value from a ArrayBuffer + * @param {boolean} notUTF8 + */ + static fromArrayBuffer(notUTF8) { + DishBigNumber.checkForValue(this.value); + try { + this.value = new BigNumber(Utils.arrayBufferToStr(this.value, !notUTF8)); + } catch (err) { + this.value = new BigNumber(NaN); + } + } +} + +export default DishBigNumber; diff --git a/src/core/dishTypes/DishByteArray.mjs b/src/core/dishTypes/DishByteArray.mjs new file mode 100644 index 00000000..12a6d334 --- /dev/null +++ b/src/core/dishTypes/DishByteArray.mjs @@ -0,0 +1,31 @@ +/** + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import DishType from "./DishType.mjs"; + +/** + * Translation methods for ArrayBuffer Dishes + */ +class DishByteArray extends DishType { + + /** + * convert the given value to a ArrayBuffer + */ + static toArrayBuffer() { + DishByteArray.checkForValue(this.value); + this.value = new Uint8Array(this.value).buffer; + } + + /** + * convert the given value from a ArrayBuffer + */ + static fromArrayBuffer() { + DishByteArray.checkForValue(this.value); + this.value = Array.prototype.slice.call(new Uint8Array(this.value)); + } +} + +export default DishByteArray; diff --git a/src/core/dishTypes/DishFile.mjs b/src/core/dishTypes/DishFile.mjs new file mode 100644 index 00000000..a22df9e5 --- /dev/null +++ b/src/core/dishTypes/DishFile.mjs @@ -0,0 +1,42 @@ +/** + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import DishType from "./DishType.mjs"; +import Utils, { isNodeEnvironment } from "../Utils.mjs"; + +/** + * Translation methods for file Dishes + */ +class DishFile extends DishType { + + /** + * convert the given value to an ArrayBuffer + * @param {File} value + */ + static toArrayBuffer() { + DishFile.checkForValue(this.value); + if (isNodeEnvironment()) { + this.value = Utils.readFileSync(this.value); + } else { + return new Promise((resolve, reject) => { + Utils.readFile(this.value) + .then(v => this.value = v.buffer) + .then(resolve) + .catch(reject); + }); + } + } + + /** + * convert the given value from an ArrayBuffer + */ + static fromArrayBuffer() { + DishFile.checkForValue(this.value); + this.value = new File(this.value, "unknown"); + } +} + +export default DishFile; diff --git a/src/core/dishTypes/DishHTML.mjs b/src/core/dishTypes/DishHTML.mjs new file mode 100644 index 00000000..854cacd0 --- /dev/null +++ b/src/core/dishTypes/DishHTML.mjs @@ -0,0 +1,26 @@ +/** + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import DishString from "./DishString.mjs"; +import Utils from "../Utils.mjs"; + +/** + * Translation methods for HTML Dishes + */ +class DishHTML extends DishString { + + /** + * convert the given value to a ArrayBuffer + * @param {String} value + */ + static toArrayBuffer() { + DishHTML.checkForValue(this.value); + this.value = this.value ? Utils.strToArrayBuffer(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : new ArrayBuffer; + } + +} + +export default DishHTML; diff --git a/src/core/dishTypes/DishJSON.mjs b/src/core/dishTypes/DishJSON.mjs new file mode 100644 index 00000000..210f32d8 --- /dev/null +++ b/src/core/dishTypes/DishJSON.mjs @@ -0,0 +1,33 @@ +/** + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import DishType from "./DishType.mjs"; +import Utils from "../Utils.mjs"; + +/** + * Translation methods for JSON dishes + */ +class DishJSON extends DishType { + + /** + * convert the given value to a ArrayBuffer + */ + static toArrayBuffer() { + DishJSON.checkForValue(this.value); + this.value = this.value ? Utils.strToArrayBuffer(JSON.stringify(this.value, null, 4)) : new ArrayBuffer; + } + + /** + * convert the given value from a ArrayBuffer + * @param {boolean} notUTF8 + */ + static fromArrayBuffer(notUTF8) { + DishJSON.checkForValue(this.value); + this.value = JSON.parse(Utils.arrayBufferToStr(this.value, !notUTF8)); + } +} + +export default DishJSON; diff --git a/src/core/dishTypes/DishListFile.mjs b/src/core/dishTypes/DishListFile.mjs new file mode 100644 index 00000000..198a974b --- /dev/null +++ b/src/core/dishTypes/DishListFile.mjs @@ -0,0 +1,58 @@ +/** + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import DishType from "./DishType.mjs"; +import { isNodeEnvironment } from "../Utils.mjs"; + + +/** + * Translation methods for ListFile Dishes + */ +class DishListFile extends DishType { + + /** + * convert the given value to a ArrayBuffer + */ + static toArrayBuffer() { + DishListFile.checkForValue(this.value); + + if (isNodeEnvironment()) { + this.value = this.value.map(file => Uint8Array.from(file.data)); + } + this.value = DishListFile.concatenateTypedArrays(...this.value).buffer; + } + + /** + * convert the given value from a ArrayBuffer + */ + static fromArrayBuffer() { + DishListFile.checkForValue(this.value); + this.value = [new File(this.value, "unknown")]; + } + + + /** + * Concatenates a list of Uint8Arrays together + * + * @param {Uint8Array[]} arrays + * @returns {Uint8Array} + */ + static concatenateTypedArrays(...arrays) { + let totalLength = 0; + for (const arr of arrays) { + totalLength += arr.length; + } + const result = new Uint8Array(totalLength); + let offset = 0; + for (const arr of arrays) { + result.set(arr, offset); + offset += arr.length; + } + return result; + } +} + +export default DishListFile; diff --git a/src/core/dishTypes/DishNumber.mjs b/src/core/dishTypes/DishNumber.mjs new file mode 100644 index 00000000..8769a69a --- /dev/null +++ b/src/core/dishTypes/DishNumber.mjs @@ -0,0 +1,34 @@ +/** + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + + +import DishType from "./DishType.mjs"; +import Utils from "../Utils.mjs"; + +/** + * Translation methods for number dishes + */ +class DishNumber extends DishType { + + /** + * convert the given value to a ArrayBuffer + */ + static toArrayBuffer() { + DishNumber.checkForValue(this.value); + this.value = typeof this.value === "number" ? Utils.strToArrayBuffer(this.value.toString()) : new ArrayBuffer; + } + + /** + * convert the given value from a ArrayBuffer + * @param {boolean} notUTF8 + */ + static fromArrayBuffer(notUTF8) { + DishNumber.checkForValue(this.value); + this.value = this.value ? parseFloat(Utils.arrayBufferToStr(this.value, !notUTF8)) : 0; + } +} + +export default DishNumber; diff --git a/src/core/dishTypes/DishString.mjs b/src/core/dishTypes/DishString.mjs new file mode 100644 index 00000000..d7768859 --- /dev/null +++ b/src/core/dishTypes/DishString.mjs @@ -0,0 +1,34 @@ +/** + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + + +import DishType from "./DishType.mjs"; +import Utils from "../Utils.mjs"; + +/** + * Translation methods for string dishes + */ +class DishString extends DishType { + + /** + * convert the given value to a ArrayBuffer + */ + static toArrayBuffer() { + DishString.checkForValue(this.value); + this.value = this.value ? Utils.strToArrayBuffer(this.value) : new ArrayBuffer; + } + + /** + * convert the given value from a ArrayBuffer + * @param {boolean} notUTF8 + */ + static fromArrayBuffer(notUTF8) { + DishString.checkForValue(this.value); + this.value = this.value ? Utils.arrayBufferToStr(this.value, !notUTF8) : ""; + } +} + +export default DishString; diff --git a/src/core/dishTypes/DishType.mjs b/src/core/dishTypes/DishType.mjs new file mode 100644 index 00000000..849b5756 --- /dev/null +++ b/src/core/dishTypes/DishType.mjs @@ -0,0 +1,39 @@ +/** + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + + +/** + * Abstract class for dish translation methods + */ +class DishType { + + /** + * Warn translations dont work without value from bind + */ + static checkForValue(value) { + if (value === undefined) { + throw new Error("only use translation methods with .bind"); + } + } + + /** + * convert the given value to a ArrayBuffer + * @param {*} value + */ + static toArrayBuffer() { + throw new Error("toArrayBuffer has not been implemented"); + } + + /** + * convert the given value from a ArrayBuffer + * @param {boolean} notUTF8 + */ + static fromArrayBuffer(notUTF8=undefined) { + throw new Error("fromArrayBuffer has not been implemented"); + } +} + +export default DishType; diff --git a/src/core/dishTypes/index.mjs b/src/core/dishTypes/index.mjs new file mode 100644 index 00000000..a3a500b0 --- /dev/null +++ b/src/core/dishTypes/index.mjs @@ -0,0 +1,26 @@ +/** + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + + +import DishByteArray from "./DishByteArray.mjs"; +import DishBigNumber from "./DishBigNumber.mjs"; +import DishFile from "./DishFile.mjs"; +import DishHTML from "./DishHTML.mjs"; +import DishJSON from "./DishJSON.mjs"; +import DishListFile from "./DishListFile.mjs"; +import DishNumber from "./DishNumber.mjs"; +import DishString from "./DishString.mjs"; + +export { + DishByteArray, + DishBigNumber, + DishFile, + DishHTML, + DishJSON, + DishListFile, + DishNumber, + DishString, +}; diff --git a/src/core/errors/ExcludedOperationError.mjs b/src/core/errors/ExcludedOperationError.mjs new file mode 100644 index 00000000..2972c31d --- /dev/null +++ b/src/core/errors/ExcludedOperationError.mjs @@ -0,0 +1,25 @@ +/** + * Custom error type for handling operation that isnt included in node.js API + * + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +class ExcludedOperationError extends Error { + /** + * Standard error constructor. Adds no new behaviour. + * + * @param args - Standard error args + */ + constructor(...args) { + super(...args); + + this.type = "ExcludedOperationError"; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, ExcludedOperationError); + } + } +} + +export default ExcludedOperationError; diff --git a/src/core/lib/Arithmetic.mjs b/src/core/lib/Arithmetic.mjs index 3ee2372f..2c56e483 100644 --- a/src/core/lib/Arithmetic.mjs +++ b/src/core/lib/Arithmetic.mjs @@ -5,7 +5,7 @@ * @license Apache-2.0 */ -import Utils from "../Utils"; +import Utils from "../Utils.mjs"; import BigNumber from "bignumber.js"; diff --git a/src/core/lib/Base64.mjs b/src/core/lib/Base64.mjs index 1eb09aa3..0d8788f9 100644 --- a/src/core/lib/Base64.mjs +++ b/src/core/lib/Base64.mjs @@ -6,7 +6,7 @@ * @license Apache-2.0 */ -import Utils from "../Utils"; +import Utils from "../Utils.mjs"; /** @@ -63,7 +63,7 @@ export function toBase64(data, alphabet="A-Za-z0-9+/=") { /** * UnBase64's the input string using the given alphabet, returning a byte array. * - * @param {byteArray} data + * @param {string} data * @param {string} [alphabet="A-Za-z0-9+/="] * @param {string} [returnType="string"] - Either "string" or "byteArray" * @param {boolean} [removeNonAlphChars=true] diff --git a/src/core/lib/Binary.mjs b/src/core/lib/Binary.mjs index ca41da48..d28691e5 100644 --- a/src/core/lib/Binary.mjs +++ b/src/core/lib/Binary.mjs @@ -6,7 +6,7 @@ * @license Apache-2.0 */ -import Utils from "../Utils"; +import Utils from "../Utils.mjs"; /** diff --git a/src/core/lib/BitwiseOp.mjs b/src/core/lib/BitwiseOp.mjs index 84a7834b..fe33c812 100644 --- a/src/core/lib/BitwiseOp.mjs +++ b/src/core/lib/BitwiseOp.mjs @@ -9,7 +9,7 @@ /** * Runs bitwise operations across the input data. * - * @param {byteArray} input + * @param {byteArray|Uint8Array} input * @param {byteArray} key * @param {function} func - The bitwise calculation to carry out * @param {boolean} nullPreserving diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs index 122edd40..8ab1604d 100644 --- a/src/core/lib/Bombe.mjs +++ b/src/core/lib/Bombe.mjs @@ -7,9 +7,9 @@ * @license Apache-2.0 */ -import OperationError from "../errors/OperationError"; -import Utils from "../Utils"; -import {Rotor, Plugboard, a2i, i2a} from "./Enigma"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; +import {Rotor, Plugboard, a2i, i2a} from "./Enigma.mjs"; /** * Convenience/optimisation subclass of Rotor diff --git a/src/core/lib/Charts.mjs b/src/core/lib/Charts.mjs index 2bb40b61..6685ee6e 100644 --- a/src/core/lib/Charts.mjs +++ b/src/core/lib/Charts.mjs @@ -5,7 +5,7 @@ * @license Apache-2.0 */ -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * @constant diff --git a/src/core/lib/Ciphers.mjs b/src/core/lib/Ciphers.mjs index 3edd6983..a4d174b5 100644 --- a/src/core/lib/Ciphers.mjs +++ b/src/core/lib/Ciphers.mjs @@ -9,7 +9,7 @@ * */ -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; import CryptoJS from "crypto-js"; /** diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index af13882d..1acb5ef4 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -8,7 +8,7 @@ import geohash from "ngeohash"; import geodesy from "geodesy"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * Co-ordinate formats diff --git a/src/core/lib/Decimal.mjs b/src/core/lib/Decimal.mjs index 846b2a94..a140fd4e 100644 --- a/src/core/lib/Decimal.mjs +++ b/src/core/lib/Decimal.mjs @@ -6,7 +6,7 @@ * @license Apache-2.0 */ -import Utils from "../Utils"; +import Utils from "../Utils.mjs"; /** diff --git a/src/core/lib/Enigma.mjs b/src/core/lib/Enigma.mjs index 39193f69..988e0c45 100644 --- a/src/core/lib/Enigma.mjs +++ b/src/core/lib/Enigma.mjs @@ -5,8 +5,8 @@ * @copyright Crown Copyright 2019 * @license Apache-2.0 */ -import OperationError from "../errors/OperationError"; -import Utils from "../Utils"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; /** * Provided default Enigma rotor set. @@ -184,10 +184,10 @@ class PairMapBase { // self-stecker return; } - if (this.map.hasOwnProperty(a)) { + if (Object.prototype.hasOwnProperty.call(this.map, a)) { throw new OperationError(`${name} connects ${pair[0]} more than once`); } - if (this.map.hasOwnProperty(b)) { + if (Object.prototype.hasOwnProperty.call(this.map, b)) { throw new OperationError(`${name} connects ${pair[1]} more than once`); } this.map[a] = b; @@ -203,7 +203,7 @@ class PairMapBase { * @returns {number} */ transform(c) { - if (!this.map.hasOwnProperty(c)) { + if (!Object.prototype.hasOwnProperty.call(this.map, c)) { return c; } return this.map[c]; diff --git a/src/core/lib/FileSignatures.mjs b/src/core/lib/FileSignatures.mjs index 61e37b88..613e82b2 100644 --- a/src/core/lib/FileSignatures.mjs +++ b/src/core/lib/FileSignatures.mjs @@ -6,7 +6,7 @@ * @license Apache-2.0 * */ -import Stream from "./Stream"; +import Stream from "./Stream.mjs"; /** * A categorised table of file types, including signatures to identify them and functions @@ -170,7 +170,7 @@ export const FILE_SIGNATURES = { mime: "image/vnd.adobe.photoshop", description: "", signature: { - 0: 0x38, + 0: 0x38, // 8BPS 1: 0x42, 2: 0x50, 3: 0x53, @@ -185,6 +185,28 @@ export const FILE_SIGNATURES = { }, extractor: null }, + { + name: "Photoshop Large Document", + extension: "psb", + mime: "application/x-photoshop", + description: "", + signature: { + 0: 0x38, // 8BPS + 1: 0x42, + 2: 0x50, + 3: 0x53, + 4: 0x0, + 5: 0x2, + 6: 0x0, + 7: 0x0, + 8: 0x0, + 9: 0x0, + 10: 0x0, + 11: 0x0, + 12: 0x0 + }, + extractor: null + }, { name: "Paint Shop Pro image", extension: "psp", @@ -233,10 +255,114 @@ export const FILE_SIGNATURES = { 5: 0x0, 6: [0x10, 0x20, 0x30, 0x40, 0x80], 7: [0x10, 0x20, 0x30, 0x40, 0x80], - 9: 0x00, + 9: 0x0, 10: [0x0, 0x1] }, extractor: null + }, + { + name: "Radiance High Dynamic Range image", + extension: "hdr", + mime: "image/vnd.radiance", + description: "", + signature: { + 0: 0x23, // #?RADIANCE + 1: 0x3f, + 2: 0x52, + 3: 0x41, + 4: 0x44, + 5: 0x49, + 6: 0x41, + 7: 0x4e, + 8: 0x43, + 9: 0x45, + 10: 0x0a + }, + extractor: null + }, + { + name: "Sony ARW image", + extension: "arw", + mime: "image/x-raw", + description: "", + signature: { + 0: 0x05, + 1: 0x0, + 2: 0x0, + 3: 0x0, + 4: 0x41, + 5: 0x57, + 6: 0x31, + 7: 0x2e + }, + extractor: null + }, + { + name: "Fujifilm Raw Image", + extension: "raf", + mime: "image/x-raw", + description: "", + signature: { + 0: 0x46, // FUJIFILMCCD-RAW + 1: 0x55, + 2: 0x4a, + 3: 0x49, + 4: 0x46, + 5: 0x49, + 6: 0x4c, + 7: 0x4d, + 8: 0x43, + 9: 0x43, + 10: 0x44, + 11: 0x2d, + 12: 0x52, + 13: 0x41, + 14: 0x57 + }, + extractor: null + }, + { + name: "Minolta RAW image", + extension: "mrw", + mime: "image/x-raw", + description: "", + signature: { + 0: 0x0, + 1: 0x4d, // MRM + 2: 0x52, + 3: 0x4d + }, + extractor: null + }, + { + name: "Adobe Bridge Thumbnail Cache", + extension: "bct", + mime: "application/octet-stream", + description: "", + signature: { + 0: 0x6c, + 1: 0x6e, + 2: 0x62, + 3: 0x74, + 4: 0x02, + 5: 0x0, + 6: 0x0, + 7: 0x0 + }, + extractor: null + }, + { + name: "Microsoft Document Imaging", + extension: "mdi", + mime: "image/vnd.ms-modi", + description: "", + signature: { + 0: 0x45, + 1: 0x50, + 2: 0x2a, + 3: 0x00 + }, + extractor: null } ], "Video": [ @@ -534,6 +660,97 @@ export const FILE_SIGNATURES = { }, extractor: null }, + { + name: "Audacity", + extension: "au", + mime: "audio/x-au", + description: "", + signature: { + 0: 0x64, // dns. + 1: 0x6e, + 2: 0x73, + 3: 0x2e, + + 24: 0x41, // AudacityBlockFile + 25: 0x75, + 26: 0x64, + 27: 0x61, + 28: 0x63, + 29: 0x69, + 30: 0x74, + 31: 0x79, + 32: 0x42, + 33: 0x6c, + 34: 0x6f, + 35: 0x63, + 36: 0x6b, + 37: 0x46, + 38: 0x69, + 39: 0x6c, + 40: 0x65 + }, + extractor: null + }, + { + name: "Audacity Block", + extension: "auf", + mime: "application/octet-stream", + description: "", + signature: { + 0: 0x41, // AudacityBlockFile + 1: 0x75, + 2: 0x64, + 3: 0x61, + 4: 0x63, + 5: 0x69, + 6: 0x74, + 7: 0x79, + 8: 0x42, + 9: 0x6c, + 10: 0x6f, + 11: 0x63, + 12: 0x6b, + 13: 0x46, + 14: 0x69, + 15: 0x6c, + 16: 0x65 + }, + extractor: null + }, + { + name: "Audio Interchange File", + extension: "aif", + mime: "audio/x-aiff", + description: "", + signature: { + 0: 0x46, // FORM + 1: 0x4f, + 2: 0x52, + 3: 0x4d, + 8: 0x41, // AIFF + 9: 0x49, + 10: 0x46, + 11: 0x46 + }, + extractor: null + }, + { + name: "Audio Interchange File (compressed)", + extension: "aifc", + mime: "audio/x-aifc", + description: "", + signature: { + 0: 0x46, // FORM + 1: 0x4f, + 2: 0x52, + 3: 0x4d, + 8: 0x41, // AIFC + 9: 0x49, + 10: 0x46, + 11: 0x43 + }, + extractor: null + } ], "Documents": [ { @@ -898,6 +1115,110 @@ export const FILE_SIGNATURES = { }, extractor: null }, + { + name: "ARJ Archive", + extension: "arj", + mime: "application/x-arj-compressed", + description: "", + signature: { + 0: 0x60, + 1: 0xea, + 8: [0x0, 0x10, 0x14], + 9: 0x0, + 10: 0x2 + }, + extractor: null + }, + { + name: "WinAce Archive", + extension: "ace", + mime: "application/x-ace-compressed", + description: "", + signature: { + 7: 0x2a, // **ACE** + 8: 0x2a, + 9: 0x41, + 10: 0x43, + 11: 0x45, + 12: 0x2a, + 13: 0x2a + }, + extractor: null + }, + { + name: "Macintosh BinHex Encoded File", + extension: "hqx", + mime: "application/mac-binhex", + description: "", + signature: { + 11: 0x6d, // must be converted with BinHex + 12: 0x75, + 13: 0x73, + 14: 0x74, + 15: 0x20, + 16: 0x62, + 17: 0x65, + 18: 0x20, + 19: 0x63, + 20: 0x6f, + 21: 0x6e, + 22: 0x76, + 23: 0x65, + 24: 0x72, + 25: 0x74, + 26: 0x65, + 27: 0x64, + 28: 0x20, + 29: 0x77, + 30: 0x69, + 31: 0x74, + 32: 0x68, + 33: 0x20, + 34: 0x42, + 35: 0x69, + 36: 0x6e, + 37: 0x48, + 38: 0x65, + 39: 0x78 + }, + extractor: null + }, + { + name: "ALZip Archive", + extension: "alz", + mime: "application/octet-stream", + description: "", + signature: { + 0: 0x41, // ALZ + 1: 0x4c, + 2: 0x5a, + 3: 0x01, + 4: 0x0a, + 5: 0x0, + 6: 0x0, + 7: 0x0 + }, + extractor: null + }, + { + name: "KGB Compressed Archive", + extension: "kgb", + mime: "application/x-kgb-compressed", + description: "", + signature: { + 0: 0x4b, // KGB_arch - + 1: 0x47, + 2: 0x42, + 3: 0x5f, + 4: 0x61, + 5: 0x72, + 6: 0x63, + 7: 0x68, + 8: 0x20, + 9: 0x2d + }, + extractor: null + } ], "Miscellaneous": [ { @@ -1041,6 +1362,43 @@ export const FILE_SIGNATURES = { }, extractor: null }, + { + name: "BitTorrent link", + extension: "torrent", + mime: "application/x-bittorrent", + description: "", + signature: [ + { + 0: 0x64, // d8:announce##: + 1: 0x38, + 2: 0x3a, + 3: 0x61, + 4: 0x6e, + 5: 0x6e, + 6: 0x6f, + 7: 0x75, + 8: 0x6e, + 9: 0x63, + 10: 0x65, + 11: 0x23, + 12: 0x23, + 13: 0x3a + }, + { + 0: 0x64, // d4:infod + 1: 0x34, + 2: 0x3a, + 3: 0x69, + 4: 0x6e, + 5: 0x66, + 6: 0x6f, + 7: 0x64, + 8: [0x34, 0x35, 0x36], + 9: 0x3a + } + ], + extractor: null + } ] }; diff --git a/src/core/lib/FileType.mjs b/src/core/lib/FileType.mjs index e961a76f..9a3de442 100644 --- a/src/core/lib/FileType.mjs +++ b/src/core/lib/FileType.mjs @@ -6,8 +6,8 @@ * @license Apache-2.0 * */ -import {FILE_SIGNATURES} from "./FileSignatures"; -import {sendStatusMessage} from "../Utils"; +import {FILE_SIGNATURES} from "./FileSignatures.mjs"; +import {sendStatusMessage} from "../Utils.mjs"; /** diff --git a/src/core/lib/Hash.mjs b/src/core/lib/Hash.mjs index 4af48d13..d572b8b0 100644 --- a/src/core/lib/Hash.mjs +++ b/src/core/lib/Hash.mjs @@ -7,8 +7,8 @@ * @license Apache-2.0 */ -import Utils from "../Utils"; -import CryptoApi from "crypto-api/src/crypto-api"; +import Utils from "../Utils.mjs"; +import CryptoApi from "crypto-api/src/crypto-api.mjs"; /** diff --git a/src/core/lib/Hex.mjs b/src/core/lib/Hex.mjs index 5ec12e86..5ae06a7e 100644 --- a/src/core/lib/Hex.mjs +++ b/src/core/lib/Hex.mjs @@ -6,13 +6,13 @@ * @license Apache-2.0 */ -import Utils from "../Utils"; +import Utils from "../Utils.mjs"; /** * Convert a byte array into a hex string. * - * @param {Uint8Array|byteArray} data + * @param {byteArray|Uint8Array|ArrayBuffer} data * @param {string} [delim=" "] * @param {number} [padding=2] * @returns {string} @@ -26,6 +26,7 @@ import Utils from "../Utils"; */ export function toHex(data, delim=" ", padding=2) { if (!data) return ""; + if (data instanceof ArrayBuffer) data = new Uint8Array(data); let output = ""; @@ -47,7 +48,7 @@ export function toHex(data, delim=" ", padding=2) { /** * Convert a byte array into a hex string as efficiently as possible with no options. * - * @param {byteArray} data + * @param {byteArray|Uint8Array|ArrayBuffer} data * @returns {string} * * @example @@ -56,6 +57,7 @@ export function toHex(data, delim=" ", padding=2) { */ export function toHexFast(data) { if (!data) return ""; + if (data instanceof ArrayBuffer) data = new Uint8Array(data); const output = []; diff --git a/src/core/lib/IP.mjs b/src/core/lib/IP.mjs index cfefbd11..f378763e 100644 --- a/src/core/lib/IP.mjs +++ b/src/core/lib/IP.mjs @@ -8,8 +8,8 @@ * @license Apache-2.0 */ -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Parses an IPv4 CIDR range (e.g. 192.168.0.0/24) and displays information about it. diff --git a/src/core/lib/ImageManipulation.mjs b/src/core/lib/ImageManipulation.mjs new file mode 100644 index 00000000..63a80fe4 --- /dev/null +++ b/src/core/lib/ImageManipulation.mjs @@ -0,0 +1,251 @@ +/** + * Image manipulation resources + * + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import OperationError from "../errors/OperationError.mjs"; + +/** + * Gaussian blurs an image. + * + * @param {jimp} input + * @param {number} radius + * @param {boolean} fast + * @returns {jimp} + */ +export function gaussianBlur (input, radius) { + try { + // From http://blog.ivank.net/fastest-gaussian-blur.html + const boxes = boxesForGauss(radius, 3); + for (let i = 0; i < 3; i++) { + input = boxBlur(input, (boxes[i] - 1) / 2); + } + } catch (err) { + throw new OperationError(`Error blurring image. (${err})`); + } + + return input; +} + +/** + * + * @param {number} radius + * @param {number} numBoxes + * @returns {Array} + */ +function boxesForGauss(radius, numBoxes) { + const idealWidth = Math.sqrt((12 * radius * radius / numBoxes) + 1); + + let wl = Math.floor(idealWidth); + + if (wl % 2 === 0) { + wl--; + } + + const wu = wl + 2; + + const mIdeal = (12 * radius * radius - numBoxes * wl * wl - 4 * numBoxes * wl - 3 * numBoxes) / (-4 * wl - 4); + const m = Math.round(mIdeal); + + const sizes = []; + for (let i = 0; i < numBoxes; i++) { + sizes.push(i < m ? wl : wu); + } + return sizes; +} + +/** + * Applies a box blur effect to the image + * + * @param {jimp} source + * @param {number} radius + * @returns {jimp} + */ +function boxBlur (source, radius) { + const width = source.bitmap.width; + const height = source.bitmap.height; + let output = source.clone(); + output = boxBlurH(source, output, width, height, radius); + source = boxBlurV(output, source, width, height, radius); + + return source; +} + +/** + * Applies the horizontal blur + * + * @param {jimp} source + * @param {jimp} output + * @param {number} width + * @param {number} height + * @param {number} radius + * @returns {jimp} + */ +function boxBlurH (source, output, width, height, radius) { + const iarr = 1 / (radius + radius + 1); + for (let i = 0; i < height; i++) { + let ti = 0, + li = ti, + ri = ti + radius; + const idx = source.getPixelIndex(ti, i); + const firstValRed = source.bitmap.data[idx], + firstValGreen = source.bitmap.data[idx + 1], + firstValBlue = source.bitmap.data[idx + 2], + firstValAlpha = source.bitmap.data[idx + 3]; + + const lastIdx = source.getPixelIndex(width - 1, i), + lastValRed = source.bitmap.data[lastIdx], + lastValGreen = source.bitmap.data[lastIdx + 1], + lastValBlue = source.bitmap.data[lastIdx + 2], + lastValAlpha = source.bitmap.data[lastIdx + 3]; + + let red = (radius + 1) * firstValRed; + let green = (radius + 1) * firstValGreen; + let blue = (radius + 1) * firstValBlue; + let alpha = (radius + 1) * firstValAlpha; + + for (let j = 0; j < radius; j++) { + const jIdx = source.getPixelIndex(ti + j, i); + red += source.bitmap.data[jIdx]; + green += source.bitmap.data[jIdx + 1]; + blue += source.bitmap.data[jIdx + 2]; + alpha += source.bitmap.data[jIdx + 3]; + } + + for (let j = 0; j <= radius; j++) { + const jIdx = source.getPixelIndex(ri++, i); + red += source.bitmap.data[jIdx] - firstValRed; + green += source.bitmap.data[jIdx + 1] - firstValGreen; + blue += source.bitmap.data[jIdx + 2] - firstValBlue; + alpha += source.bitmap.data[jIdx + 3] - firstValAlpha; + + const tiIdx = source.getPixelIndex(ti++, i); + output.bitmap.data[tiIdx] = Math.round(red * iarr); + output.bitmap.data[tiIdx + 1] = Math.round(green * iarr); + output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr); + output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr); + } + + for (let j = radius + 1; j < width - radius; j++) { + const riIdx = source.getPixelIndex(ri++, i); + const liIdx = source.getPixelIndex(li++, i); + red += source.bitmap.data[riIdx] - source.bitmap.data[liIdx]; + green += source.bitmap.data[riIdx + 1] - source.bitmap.data[liIdx + 1]; + blue += source.bitmap.data[riIdx + 2] - source.bitmap.data[liIdx + 2]; + alpha += source.bitmap.data[riIdx + 3] - source.bitmap.data[liIdx + 3]; + + const tiIdx = source.getPixelIndex(ti++, i); + output.bitmap.data[tiIdx] = Math.round(red * iarr); + output.bitmap.data[tiIdx + 1] = Math.round(green * iarr); + output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr); + output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr); + } + + for (let j = width - radius; j < width; j++) { + const liIdx = source.getPixelIndex(li++, i); + red += lastValRed - source.bitmap.data[liIdx]; + green += lastValGreen - source.bitmap.data[liIdx + 1]; + blue += lastValBlue - source.bitmap.data[liIdx + 2]; + alpha += lastValAlpha - source.bitmap.data[liIdx + 3]; + + const tiIdx = source.getPixelIndex(ti++, i); + output.bitmap.data[tiIdx] = Math.round(red * iarr); + output.bitmap.data[tiIdx + 1] = Math.round(green * iarr); + output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr); + output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr); + } + } + return output; +} + +/** + * Applies the vertical blur + * + * @param {jimp} source + * @param {jimp} output + * @param {number} width + * @param {number} height + * @param {number} radius + * @returns {jimp} + */ +function boxBlurV (source, output, width, height, radius) { + const iarr = 1 / (radius + radius + 1); + for (let i = 0; i < width; i++) { + let ti = 0, + li = ti, + ri = ti + radius; + + const idx = source.getPixelIndex(i, ti); + + const firstValRed = source.bitmap.data[idx], + firstValGreen = source.bitmap.data[idx + 1], + firstValBlue = source.bitmap.data[idx + 2], + firstValAlpha = source.bitmap.data[idx + 3]; + + const lastIdx = source.getPixelIndex(i, height - 1), + lastValRed = source.bitmap.data[lastIdx], + lastValGreen = source.bitmap.data[lastIdx + 1], + lastValBlue = source.bitmap.data[lastIdx + 2], + lastValAlpha = source.bitmap.data[lastIdx + 3]; + + let red = (radius + 1) * firstValRed; + let green = (radius + 1) * firstValGreen; + let blue = (radius + 1) * firstValBlue; + let alpha = (radius + 1) * firstValAlpha; + + for (let j = 0; j < radius; j++) { + const jIdx = source.getPixelIndex(i, ti + j); + red += source.bitmap.data[jIdx]; + green += source.bitmap.data[jIdx + 1]; + blue += source.bitmap.data[jIdx + 2]; + alpha += source.bitmap.data[jIdx + 3]; + } + + for (let j = 0; j <= radius; j++) { + const riIdx = source.getPixelIndex(i, ri++); + red += source.bitmap.data[riIdx] - firstValRed; + green += source.bitmap.data[riIdx + 1] - firstValGreen; + blue += source.bitmap.data[riIdx + 2] - firstValBlue; + alpha += source.bitmap.data[riIdx + 3] - firstValAlpha; + + const tiIdx = source.getPixelIndex(i, ti++); + output.bitmap.data[tiIdx] = Math.round(red * iarr); + output.bitmap.data[tiIdx + 1] = Math.round(green * iarr); + output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr); + output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr); + } + + for (let j = radius + 1; j < height - radius; j++) { + const riIdx = source.getPixelIndex(i, ri++); + const liIdx = source.getPixelIndex(i, li++); + red += source.bitmap.data[riIdx] - source.bitmap.data[liIdx]; + green += source.bitmap.data[riIdx + 1] - source.bitmap.data[liIdx + 1]; + blue += source.bitmap.data[riIdx + 2] - source.bitmap.data[liIdx + 2]; + alpha += source.bitmap.data[riIdx + 3] - source.bitmap.data[liIdx + 3]; + + const tiIdx = source.getPixelIndex(i, ti++); + output.bitmap.data[tiIdx] = Math.round(red * iarr); + output.bitmap.data[tiIdx + 1] = Math.round(green * iarr); + output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr); + output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr); + } + + for (let j = height - radius; j < height; j++) { + const liIdx = source.getPixelIndex(i, li++); + red += lastValRed - source.bitmap.data[liIdx]; + green += lastValGreen - source.bitmap.data[liIdx + 1]; + blue += lastValBlue - source.bitmap.data[liIdx + 2]; + alpha += lastValAlpha - source.bitmap.data[liIdx + 3]; + + const tiIdx = source.getPixelIndex(i, ti++); + output.bitmap.data[tiIdx] = Math.round(red * iarr); + output.bitmap.data[tiIdx + 1] = Math.round(green * iarr); + output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr); + output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr); + } + } + return output; +} diff --git a/src/core/lib/Magic.mjs b/src/core/lib/Magic.mjs index f0b55857..f6802e97 100644 --- a/src/core/lib/Magic.mjs +++ b/src/core/lib/Magic.mjs @@ -1,8 +1,8 @@ import OperationConfig from "../config/OperationConfig.json"; -import Utils from "../Utils"; -import Recipe from "../Recipe"; -import Dish from "../Dish"; -import {detectFileType} from "./FileType"; +import Utils, { isWorkerEnvironment } from "../Utils.mjs"; +import Recipe from "../Recipe.mjs"; +import Dish from "../Dish.mjs"; +import {detectFileType} from "./FileType.mjs"; import chiSquared from "chi-squared"; /** @@ -312,6 +312,11 @@ class Magic { return; } + // If the recipe returned an empty buffer, do not continue + if (_buffersEqual(output, new ArrayBuffer())) { + return; + } + const magic = new Magic(output, this.opPatterns), speculativeResults = await magic.speculativeExecution( depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful, crib); @@ -333,7 +338,7 @@ class Magic { } // Prune branches that result in unhelpful outputs - results = results.filter(r => + const prunedResults = results.filter(r => (r.useful || r.data.length > 0) && // The operation resulted in "" ( // One of the following must be true r.languageScores[0].probability > 0 || // Some kind of language was found @@ -344,7 +349,7 @@ class Magic { ); // Return a sorted list of possible recipes along with their properties - return results.sort((a, b) => { + return prunedResults.sort((a, b) => { // Each option is sorted based on its most likely language (lower is better) let aScore = a.languageScores[0].score, bScore = b.languageScores[0].score; @@ -390,12 +395,17 @@ class Magic { const dish = new Dish(); dish.set(input, Dish.ARRAY_BUFFER); - if (ENVIRONMENT_IS_WORKER()) self.loadRequiredModules(recipeConfig); + if (isWorkerEnvironment()) self.loadRequiredModules(recipeConfig); const recipe = new Recipe(recipeConfig); try { await recipe.execute(dish); - return dish.get(Dish.ARRAY_BUFFER); + // Return an empty buffer if the recipe did not run to completion + if (recipe.lastRunOp === recipe.opList[recipe.opList.length - 1]) { + return dish.get(Dish.ARRAY_BUFFER); + } else { + return new ArrayBuffer(); + } } catch (err) { // If there are errors, return an empty buffer return new ArrayBuffer(); @@ -440,7 +450,7 @@ class Magic { const opPatterns = []; for (const op in OperationConfig) { - if (!OperationConfig[op].hasOwnProperty("patterns")) continue; + if (!("patterns" in OperationConfig[op])) continue; OperationConfig[op].patterns.forEach(pattern => { opPatterns.push({ diff --git a/src/core/lib/PGP.mjs b/src/core/lib/PGP.mjs index 1654b380..cbb31b92 100644 --- a/src/core/lib/PGP.mjs +++ b/src/core/lib/PGP.mjs @@ -10,7 +10,8 @@ * */ -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; import kbpgp from "kbpgp"; import * as es6promisify from "es6-promisify"; const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify; @@ -45,7 +46,7 @@ export const ASP = kbpgp.ASP({ msg = `Stage: ${info.what}`; } - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage(msg); } }); diff --git a/src/core/lib/Protobuf.mjs b/src/core/lib/Protobuf.mjs index 408a481b..03d5943e 100644 --- a/src/core/lib/Protobuf.mjs +++ b/src/core/lib/Protobuf.mjs @@ -1,4 +1,4 @@ -import Utils from "../Utils"; +import Utils from "../Utils.mjs"; /** * Protobuf lib. Contains functions to decode protobuf serialised @@ -16,14 +16,14 @@ class Protobuf { /** * Protobuf constructor * - * @param {byteArray} data + * @param {byteArray|Uint8Array} data */ constructor(data) { - // Check we have a byteArray - if (data instanceof Array) { + // Check we have a byteArray or Uint8Array + if (data instanceof Array || data instanceof Uint8Array) { this.data = data; } else { - throw new Error("Protobuf input must be a byteArray"); + throw new Error("Protobuf input must be a byteArray or Uint8Array"); } // Set up masks @@ -124,7 +124,7 @@ class Protobuf { // Get the field key/values const key = field.key; const value = field.value; - object[key] = object.hasOwnProperty(key) ? + object[key] = Object.prototype.hasOwnProperty.call(object, key) ? object[key] instanceof Array ? object[key].concat([value]) : [object[key], value] : diff --git a/src/core/lib/PublicKey.mjs b/src/core/lib/PublicKey.mjs index ece567a0..9ec990fe 100644 --- a/src/core/lib/PublicKey.mjs +++ b/src/core/lib/PublicKey.mjs @@ -6,7 +6,7 @@ * @license Apache-2.0 */ -import { toHex, fromHex } from "./Hex"; +import { toHex, fromHex } from "./Hex.mjs"; /** * Formats Distinguished Name (DN) strings. diff --git a/src/core/lib/QRCode.mjs b/src/core/lib/QRCode.mjs new file mode 100644 index 00000000..1585cb41 --- /dev/null +++ b/src/core/lib/QRCode.mjs @@ -0,0 +1,93 @@ +/** + * QR code resources + * + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import OperationError from "../errors/OperationError.mjs"; +import jsQR from "jsqr"; +import qr from "qr-image"; +import jimp from "jimp"; +import Utils from "../Utils.mjs"; + +/** + * Parses a QR code image from an image + * + * @param {ArrayBuffer} input + * @param {boolean} normalise + * @returns {string} + */ +export async function parseQrCode(input, normalise) { + let image; + try { + image = await jimp.read(input); + } catch (err) { + throw new OperationError(`Error opening image. (${err})`); + } + + try { + if (normalise) { + image.rgba(false); + image.background(0xFFFFFFFF); + image.normalize(); + image.greyscale(); + image = await image.getBufferAsync(jimp.MIME_JPEG); + image = await jimp.read(image); + } + } catch (err) { + throw new OperationError(`Error normalising iamge. (${err})`); + } + + const qrData = jsQR(image.bitmap.data, image.getWidth(), image.getHeight()); + if (qrData) { + return qrData.data; + } else { + throw new OperationError("Could not read a QR code from the image."); + } +} + +/** + * Generates a QR code from the input string + * + * @param {string} input + * @param {string} format + * @param {number} moduleSize + * @param {number} margin + * @param {string} errorCorrection + * @returns {ArrayBuffer} + */ +export function generateQrCode(input, format, moduleSize, margin, errorCorrection) { + const formats = ["SVG", "EPS", "PDF", "PNG"]; + if (!formats.includes(format.toUpperCase())) { + throw new OperationError("Unsupported QR code format."); + } + + let qrImage; + try { + qrImage = qr.imageSync(input, { + type: format, + size: moduleSize, + margin: margin, + "ec_level": errorCorrection.charAt(0).toUpperCase() + }); + } catch (err) { + throw new OperationError(`Error generating QR code. (${err})`); + } + + if (!qrImage) { + throw new OperationError("Error generating QR code."); + } + + switch (format) { + case "SVG": + case "EPS": + case "PDF": + return Utils.strToArrayBuffer(qrImage); + case "PNG": + return qrImage.buffer; + default: + throw new OperationError("Unsupported QR code format."); + } +} diff --git a/src/core/lib/TLVParser.mjs b/src/core/lib/TLVParser.mjs index 9e9395ff..cb8432c1 100644 --- a/src/core/lib/TLVParser.mjs +++ b/src/core/lib/TLVParser.mjs @@ -21,7 +21,7 @@ export default class TLVParser { /** * TLVParser constructor * - * @param {byteArray} input + * @param {byteArray|Uint8Array} input * @param {Object} options */ constructor(input, options) { diff --git a/src/core/lib/Typex.mjs b/src/core/lib/Typex.mjs index 484a1e6b..39bdee5d 100644 --- a/src/core/lib/Typex.mjs +++ b/src/core/lib/Typex.mjs @@ -6,9 +6,9 @@ * @copyright Crown Copyright 2019 * @license Apache-2.0 */ -import OperationError from "../errors/OperationError"; -import * as Enigma from "../lib/Enigma"; -import Utils from "../Utils"; +import OperationError from "../errors/OperationError.mjs"; +import * as Enigma from "../lib/Enigma.mjs"; +import Utils from "../Utils.mjs"; /** * A set of example Typex rotors. No Typex rotor wirings are publicly available, so these are @@ -98,14 +98,14 @@ export class TypexMachine extends Enigma.EnigmaBase { if (x === " ") { inputMod += "X"; } else if (mode) { - if (KEYBOARD_REV.hasOwnProperty(x)) { + if (Object.prototype.hasOwnProperty.call(KEYBOARD_REV, x)) { inputMod += KEYBOARD_REV[x]; } else { mode = false; inputMod += "V" + x; } } else { - if (KEYBOARD_REV.hasOwnProperty(x)) { + if (Object.prototype.hasOwnProperty.call(KEYBOARD_REV, x)) { mode = true; inputMod += "Z" + KEYBOARD_REV[x]; } else { diff --git a/src/core/lib/Zlib.mjs b/src/core/lib/Zlib.mjs index 7cfb4c2f..780fd3d7 100644 --- a/src/core/lib/Zlib.mjs +++ b/src/core/lib/Zlib.mjs @@ -6,7 +6,7 @@ * @license Apache-2.0 */ -import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min"; +import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min.js"; const Zlib = zlibAndGzip.Zlib; diff --git a/src/core/operations/A1Z26CipherDecode.mjs b/src/core/operations/A1Z26CipherDecode.mjs index 2a9f9ce7..0b113945 100644 --- a/src/core/operations/A1Z26CipherDecode.mjs +++ b/src/core/operations/A1Z26CipherDecode.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {DELIM_OPTIONS} from "../lib/Delim"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {DELIM_OPTIONS} from "../lib/Delim.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * A1Z26 Cipher Decode operation diff --git a/src/core/operations/A1Z26CipherEncode.mjs b/src/core/operations/A1Z26CipherEncode.mjs index d1202d83..fc562b9e 100644 --- a/src/core/operations/A1Z26CipherEncode.mjs +++ b/src/core/operations/A1Z26CipherEncode.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {DELIM_OPTIONS} from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {DELIM_OPTIONS} from "../lib/Delim.mjs"; /** * A1Z26 Cipher Encode operation diff --git a/src/core/operations/ADD.mjs b/src/core/operations/ADD.mjs index dc593940..78580688 100644 --- a/src/core/operations/ADD.mjs +++ b/src/core/operations/ADD.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import { bitOp, add, BITWISE_OP_DELIMS } from "../lib/BitwiseOp"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import { bitOp, add, BITWISE_OP_DELIMS } from "../lib/BitwiseOp.mjs"; /** * ADD operation diff --git a/src/core/operations/AESDecrypt.mjs b/src/core/operations/AESDecrypt.mjs index 53489d3c..a786bdaf 100644 --- a/src/core/operations/AESDecrypt.mjs +++ b/src/core/operations/AESDecrypt.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; import forge from "node-forge/dist/forge.min.js"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * AES Decrypt operation diff --git a/src/core/operations/AESEncrypt.mjs b/src/core/operations/AESEncrypt.mjs index 5103cb86..7375e308 100644 --- a/src/core/operations/AESEncrypt.mjs +++ b/src/core/operations/AESEncrypt.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; import forge from "node-forge/dist/forge.min.js"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * AES Encrypt operation @@ -65,8 +65,8 @@ class AESEncrypt extends Operation { * @throws {OperationError} if invalid key length */ run(input, args) { - const key = Utils.convertToByteArray(args[0].string, args[0].option), - iv = Utils.convertToByteArray(args[1].string, args[1].option), + const key = Utils.convertToByteString(args[0].string, args[0].option), + iv = Utils.convertToByteString(args[1].string, args[1].option), mode = args[2], inputType = args[3], outputType = args[4]; diff --git a/src/core/operations/AND.mjs b/src/core/operations/AND.mjs index 1fa84074..6bbc8366 100644 --- a/src/core/operations/AND.mjs +++ b/src/core/operations/AND.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import { bitOp, and, BITWISE_OP_DELIMS } from "../lib/BitwiseOp"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import { bitOp, and, BITWISE_OP_DELIMS } from "../lib/BitwiseOp.mjs"; /** * AND operation diff --git a/src/core/operations/AddLineNumbers.mjs b/src/core/operations/AddLineNumbers.mjs index 7e53d685..c1c6159a 100644 --- a/src/core/operations/AddLineNumbers.mjs +++ b/src/core/operations/AddLineNumbers.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Add line numbers operation diff --git a/src/core/operations/AddTextToImage.mjs b/src/core/operations/AddTextToImage.mjs new file mode 100644 index 00000000..3746c18d --- /dev/null +++ b/src/core/operations/AddTextToImage.mjs @@ -0,0 +1,267 @@ +/** + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; +import jimp from "jimp"; + +/** + * Add Text To Image operation + */ +class AddTextToImage extends Operation { + + /** + * AddTextToImage constructor + */ + constructor() { + super(); + + this.name = "Add Text To Image"; + this.module = "Image"; + this.description = "Adds text onto an image.

Text can be horizontally or vertically aligned, or the position can be manually specified.
Variants of the Roboto font face are available in any size or colour."; + this.infoURL = ""; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; + this.presentType = "html"; + this.args = [ + { + name: "Text", + type: "string", + value: "" + }, + { + name: "Horizontal align", + type: "option", + value: ["None", "Left", "Center", "Right"] + }, + { + name: "Vertical align", + type: "option", + value: ["None", "Top", "Middle", "Bottom"] + }, + { + name: "X position", + type: "number", + value: 0 + }, + { + name: "Y position", + type: "number", + value: 0 + }, + { + name: "Size", + type: "number", + value: 32, + min: 8 + }, + { + name: "Font face", + type: "option", + value: [ + "Roboto", + "Roboto Black", + "Roboto Mono", + "Roboto Slab" + ] + }, + { + name: "Red", + type: "number", + value: 255, + min: 0, + max: 255 + }, + { + name: "Green", + type: "number", + value: 255, + min: 0, + max: 255 + }, + { + name: "Blue", + type: "number", + value: 255, + min: 0, + max: 255 + }, + { + name: "Alpha", + type: "number", + value: 255, + min: 0, + max: 255 + } + ]; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {byteArray} + */ + async run(input, args) { + const text = args[0], + hAlign = args[1], + vAlign = args[2], + size = args[5], + fontFace = args[6], + red = args[7], + green = args[8], + blue = args[9], + alpha = args[10]; + + let xPos = args[3], + yPos = args[4]; + + if (!isImage(new Uint8Array(input))) { + throw new OperationError("Invalid file type."); + } + + let image; + try { + image = await jimp.read(input); + } catch (err) { + throw new OperationError(`Error loading image. (${err})`); + } + try { + if (isWorkerEnvironment()) + self.sendStatusMessage("Adding text to image..."); + + const fontsMap = {}; + const fonts = [ + import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.fnt"), + import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoBlack72White.fnt"), + import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoMono72White.fnt"), + import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoSlab72White.fnt") + ]; + + await Promise.all(fonts) + .then(fonts => { + fontsMap.Roboto = fonts[0]; + fontsMap["Roboto Black"] = fonts[1]; + fontsMap["Roboto Mono"] = fonts[2]; + fontsMap["Roboto Slab"] = fonts[3]; + }); + + + // Make Webpack load the png font images + await Promise.all([ + import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.png"), + import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoSlab72White.png"), + import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoMono72White.png"), + import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoBlack72White.png") + ]); + + const font = fontsMap[fontFace]; + + // LoadFont needs an absolute url, so append the font name to self.docURL + const jimpFont = await jimp.loadFont(self.docURL + "/" + font.default); + + jimpFont.pages.forEach(function(page) { + if (page.bitmap) { + // Adjust the RGB values of the image pages to change the font colour. + const pageWidth = page.bitmap.width; + const pageHeight = page.bitmap.height; + for (let ix = 0; ix < pageWidth; ix++) { + for (let iy = 0; iy < pageHeight; iy++) { + const idx = (iy * pageWidth + ix) << 2; + + const newRed = page.bitmap.data[idx] - (255 - red); + const newGreen = page.bitmap.data[idx + 1] - (255 - green); + const newBlue = page.bitmap.data[idx + 2] - (255 - blue); + const newAlpha = page.bitmap.data[idx + 3] - (255 - alpha); + + // Make sure the bitmap values don't go below 0 as that makes jimp very unhappy + page.bitmap.data[idx] = (newRed > 0) ? newRed : 0; + page.bitmap.data[idx + 1] = (newGreen > 0) ? newGreen : 0; + page.bitmap.data[idx + 2] = (newBlue > 0) ? newBlue : 0; + page.bitmap.data[idx + 3] = (newAlpha > 0) ? newAlpha : 0; + } + } + } + }); + + // Create a temporary image to hold the rendered text + const textImage = new jimp(jimp.measureText(jimpFont, text), jimp.measureTextHeight(jimpFont, text)); + textImage.print(jimpFont, 0, 0, text); + + // Scale the rendered text image to the correct size + const scaleFactor = size / 72; + if (size !== 1) { + // Use bicubic for decreasing size + if (size > 1) { + textImage.scale(scaleFactor, jimp.RESIZE_BICUBIC); + } else { + textImage.scale(scaleFactor, jimp.RESIZE_BILINEAR); + } + } + + // If using the alignment options, calculate the pixel values AFTER the image has been scaled + switch (hAlign) { + case "Left": + xPos = 0; + break; + case "Center": + xPos = (image.getWidth() / 2) - (textImage.getWidth() / 2); + break; + case "Right": + xPos = image.getWidth() - textImage.getWidth(); + break; + } + + switch (vAlign) { + case "Top": + yPos = 0; + break; + case "Middle": + yPos = (image.getHeight() / 2) - (textImage.getHeight() / 2); + break; + case "Bottom": + yPos = image.getHeight() - textImage.getHeight(); + break; + } + + // Blit the rendered text image onto the original source image + image.blit(textImage, xPos, yPos); + + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; + } catch (err) { + throw new OperationError(`Error adding text to image. (${err})`); + } + } + + /** + * Displays the blurred image using HTML for web apps + * + * @param {ArrayBuffer} data + * @returns {html} + */ + present(data) { + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); + + const type = isImage(dataArray); + if (!type) { + throw new OperationError("Invalid file type."); + } + + return ``; + } + +} + +export default AddTextToImage; diff --git a/src/core/operations/Adler32Checksum.mjs b/src/core/operations/Adler32Checksum.mjs index ad4b4072..80e6e340 100644 --- a/src/core/operations/Adler32Checksum.mjs +++ b/src/core/operations/Adler32Checksum.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * Adler-32 Checksum operation @@ -22,13 +22,13 @@ class Adler32Checksum extends Operation { this.module = "Crypto"; this.description = "Adler-32 is a checksum algorithm which was invented by Mark Adler in 1995, and is a modification of the Fletcher checksum. Compared to a cyclic redundancy check of the same length, it trades reliability for speed (preferring the latter).

Adler-32 is more reliable than Fletcher-16, and slightly less reliable than Fletcher-32."; this.infoURL = "https://wikipedia.org/wiki/Adler-32"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ @@ -36,6 +36,7 @@ class Adler32Checksum extends Operation { const MOD_ADLER = 65521; let a = 1, b = 0; + input = new Uint8Array(input); for (let i = 0; i < input.length; i++) { a += input[i]; diff --git a/src/core/operations/AffineCipherDecode.mjs b/src/core/operations/AffineCipherDecode.mjs index 173ccb8f..869f231a 100644 --- a/src/core/operations/AffineCipherDecode.mjs +++ b/src/core/operations/AffineCipherDecode.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Affine Cipher Decode operation diff --git a/src/core/operations/AffineCipherEncode.mjs b/src/core/operations/AffineCipherEncode.mjs index 4adc64e3..a9462ae8 100644 --- a/src/core/operations/AffineCipherEncode.mjs +++ b/src/core/operations/AffineCipherEncode.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { affineEncode } from "../lib/Ciphers"; +import Operation from "../Operation.mjs"; +import { affineEncode } from "../lib/Ciphers.mjs"; /** * Affine Cipher Encode operation diff --git a/src/core/operations/AnalyseHash.mjs b/src/core/operations/AnalyseHash.mjs index 17f78f30..72c80840 100644 --- a/src/core/operations/AnalyseHash.mjs +++ b/src/core/operations/AnalyseHash.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Analyse hash operation diff --git a/src/core/operations/AtbashCipher.mjs b/src/core/operations/AtbashCipher.mjs index fc6bf909..5635b5b9 100644 --- a/src/core/operations/AtbashCipher.mjs +++ b/src/core/operations/AtbashCipher.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { affineEncode } from "../lib/Ciphers"; +import Operation from "../Operation.mjs"; +import { affineEncode } from "../lib/Ciphers.mjs"; /** * Atbash Cipher operation diff --git a/src/core/operations/BLAKE2b.mjs b/src/core/operations/BLAKE2b.mjs index 60bca3b5..113cbaf3 100644 --- a/src/core/operations/BLAKE2b.mjs +++ b/src/core/operations/BLAKE2b.mjs @@ -4,11 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import blakejs from "blakejs"; -import OperationError from "../errors/OperationError"; -import Utils from "../Utils"; -import { toBase64 } from "../lib/Base64"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; /** * BLAKE2b operation diff --git a/src/core/operations/BLAKE2s.mjs b/src/core/operations/BLAKE2s.mjs index 72cf1988..36015607 100644 --- a/src/core/operations/BLAKE2s.mjs +++ b/src/core/operations/BLAKE2s.mjs @@ -4,11 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import blakejs from "blakejs"; -import OperationError from "../errors/OperationError"; -import Utils from "../Utils"; -import { toBase64 } from "../lib/Base64"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; /** * BLAKE2s Operation diff --git a/src/core/operations/BSONDeserialise.mjs b/src/core/operations/BSONDeserialise.mjs index 3a815285..a21eaadd 100644 --- a/src/core/operations/BSONDeserialise.mjs +++ b/src/core/operations/BSONDeserialise.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import bson from "bson"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * BSON deserialise operation diff --git a/src/core/operations/BSONSerialise.mjs b/src/core/operations/BSONSerialise.mjs index 6c91a678..6d33c6be 100644 --- a/src/core/operations/BSONSerialise.mjs +++ b/src/core/operations/BSONSerialise.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import bson from "bson"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * BSON serialise operation diff --git a/src/core/operations/Bcrypt.mjs b/src/core/operations/Bcrypt.mjs index 36a20607..53b8a92e 100644 --- a/src/core/operations/Bcrypt.mjs +++ b/src/core/operations/Bcrypt.mjs @@ -4,8 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import bcrypt from "bcryptjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; /** * Bcrypt operation @@ -44,7 +45,7 @@ class Bcrypt extends Operation { return await bcrypt.hash(input, salt, null, p => { // Progress callback - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`); }); diff --git a/src/core/operations/BcryptCompare.mjs b/src/core/operations/BcryptCompare.mjs index 5d8c393e..8d9a6937 100644 --- a/src/core/operations/BcryptCompare.mjs +++ b/src/core/operations/BcryptCompare.mjs @@ -4,8 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import bcrypt from "bcryptjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; + /** * Bcrypt compare operation @@ -43,7 +45,7 @@ class BcryptCompare extends Operation { const match = await bcrypt.compare(input, hash, null, p => { // Progress callback - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`); }); diff --git a/src/core/operations/BcryptParse.mjs b/src/core/operations/BcryptParse.mjs index 629eb1c2..600a7dc3 100644 --- a/src/core/operations/BcryptParse.mjs +++ b/src/core/operations/BcryptParse.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; import bcrypt from "bcryptjs"; /** diff --git a/src/core/operations/BifidCipherDecode.mjs b/src/core/operations/BifidCipherDecode.mjs index b55d2e26..29318a32 100644 --- a/src/core/operations/BifidCipherDecode.mjs +++ b/src/core/operations/BifidCipherDecode.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { genPolybiusSquare } from "../lib/Ciphers"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import { genPolybiusSquare } from "../lib/Ciphers.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Bifid Cipher Decode operation diff --git a/src/core/operations/BifidCipherEncode.mjs b/src/core/operations/BifidCipherEncode.mjs index 2db6279c..db38a3f2 100644 --- a/src/core/operations/BifidCipherEncode.mjs +++ b/src/core/operations/BifidCipherEncode.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { genPolybiusSquare } from "../lib/Ciphers"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { genPolybiusSquare } from "../lib/Ciphers.mjs"; /** * Bifid Cipher Encode operation diff --git a/src/core/operations/BitShiftLeft.mjs b/src/core/operations/BitShiftLeft.mjs index ceb19350..cd9f4568 100644 --- a/src/core/operations/BitShiftLeft.mjs +++ b/src/core/operations/BitShiftLeft.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Bit shift left operation @@ -21,8 +21,8 @@ class BitShiftLeft extends Operation { this.module = "Default"; this.description = "Shifts the bits in each byte towards the left by the specified amount."; this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bit_shifts"; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.args = [ { "name": "Amount", @@ -33,16 +33,17 @@ class BitShiftLeft extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args - * @returns {byteArray} + * @returns {ArrayBuffer} */ run(input, args) { const amount = args[0]; + input = new Uint8Array(input); return input.map(b => { return (b << amount) & 0xff; - }); + }).buffer; } /** diff --git a/src/core/operations/BitShiftRight.mjs b/src/core/operations/BitShiftRight.mjs index c6e4698f..2d70849e 100644 --- a/src/core/operations/BitShiftRight.mjs +++ b/src/core/operations/BitShiftRight.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Bit shift right operation @@ -21,8 +21,8 @@ class BitShiftRight extends Operation { this.module = "Default"; this.description = "Shifts the bits in each byte towards the right by the specified amount.

Logical shifts replace the leftmost bits with zeros.
Arithmetic shifts preserve the most significant bit (MSB) of the original byte keeping the sign the same (positive or negative)."; this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bit_shifts"; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.args = [ { "name": "Amount", @@ -38,18 +38,19 @@ class BitShiftRight extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args - * @returns {byteArray} + * @returns {ArrayBuffer} */ run(input, args) { const amount = args[0], type = args[1], mask = type === "Logical shift" ? 0 : 0x80; + input = new Uint8Array(input); return input.map(b => { return (b >>> amount) ^ (b & mask); - }); + }).buffer; } /** diff --git a/src/core/operations/BlowfishDecrypt.mjs b/src/core/operations/BlowfishDecrypt.mjs index 4aa08af8..fdab711e 100644 --- a/src/core/operations/BlowfishDecrypt.mjs +++ b/src/core/operations/BlowfishDecrypt.mjs @@ -4,12 +4,12 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; -import { Blowfish } from "../vendor/Blowfish"; -import { toBase64 } from "../lib/Base64"; -import { toHexFast } from "../lib/Hex"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { Blowfish } from "../vendor/Blowfish.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; +import { toHexFast } from "../lib/Hex.mjs"; /** * Lookup table for Blowfish output types. diff --git a/src/core/operations/BlowfishEncrypt.mjs b/src/core/operations/BlowfishEncrypt.mjs index 813c359c..44dfdc7e 100644 --- a/src/core/operations/BlowfishEncrypt.mjs +++ b/src/core/operations/BlowfishEncrypt.mjs @@ -4,11 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; -import { Blowfish } from "../vendor/Blowfish"; -import { toBase64 } from "../lib/Base64"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { Blowfish } from "../vendor/Blowfish.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; /** * Lookup table for Blowfish output types. diff --git a/src/core/operations/BlurImage.mjs b/src/core/operations/BlurImage.mjs index e1a52710..8561f49d 100644 --- a/src/core/operations/BlurImage.mjs +++ b/src/core/operations/BlurImage.mjs @@ -4,11 +4,13 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; -import { toBase64 } from "../lib/Base64"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; +import { isImage } from "../lib/FileType.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; import jimp from "jimp"; +import { gaussianBlur } from "../lib/ImageManipulation.mjs"; /** * Blur Image operation @@ -25,8 +27,8 @@ class BlurImage extends Operation { this.module = "Image"; this.description = "Applies a blur effect to the image.

Gaussian blur is much slower than fast blur, but produces better results."; this.infoURL = "https://wikipedia.org/wiki/Gaussian_blur"; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -44,37 +46,44 @@ class BlurImage extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ async run(input, args) { const [blurAmount, blurType] = args; - if (!isImage(input)) { + if (!isImage(new Uint8Array(input))) { throw new OperationError("Invalid file type."); } let image; try { - image = await jimp.read(Buffer.from(input)); + image = await jimp.read(input); } catch (err) { throw new OperationError(`Error loading image. (${err})`); } try { switch (blurType){ case "Fast": + if (isWorkerEnvironment()) + self.sendStatusMessage("Fast blurring image..."); image.blur(blurAmount); break; case "Gaussian": - if (ENVIRONMENT_IS_WORKER()) - self.sendStatusMessage("Gaussian blurring image. This may take a while..."); - image.gaussian(blurAmount); + if (isWorkerEnvironment()) + self.sendStatusMessage("Gaussian blurring image..."); + image = gaussianBlur(image, blurAmount); break; } - const imageBuffer = await image.getBufferAsync(jimp.AUTO); - return [...imageBuffer]; + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; } catch (err) { throw new OperationError(`Error blurring image. (${err})`); } @@ -83,18 +92,19 @@ class BlurImage extends Operation { /** * Displays the blurred image using HTML for web apps * - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/Bombe.mjs b/src/core/operations/Bombe.mjs index c2ea82bf..1718a0a4 100644 --- a/src/core/operations/Bombe.mjs +++ b/src/core/operations/Bombe.mjs @@ -6,10 +6,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import {BombeMachine} from "../lib/Bombe"; -import {ROTORS, ROTORS_FOURTH, REFLECTORS, Reflector} from "../lib/Enigma"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; +import { BombeMachine } from "../lib/Bombe.mjs"; +import { ROTORS, ROTORS_FOURTH, REFLECTORS, Reflector } from "../lib/Enigma.mjs"; /** * Bombe operation @@ -139,7 +140,7 @@ class Bombe extends Operation { const ciphertext = input.slice(offset); const reflector = new Reflector(reflectorstr); let update; - if (ENVIRONMENT_IS_WORKER()) { + if (isWorkerEnvironment()) { update = this.updateStatus; } else { update = undefined; diff --git a/src/core/operations/Bzip2Compress.mjs b/src/core/operations/Bzip2Compress.mjs new file mode 100644 index 00000000..45dbfae6 --- /dev/null +++ b/src/core/operations/Bzip2Compress.mjs @@ -0,0 +1,73 @@ +/** + * @author Matt C [me@mitt.dev] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Bzip2 from "libbzip2-wasm"; +import { isWorkerEnvironment } from "../Utils.mjs"; + +/** + * Bzip2 Compress operation + */ +class Bzip2Compress extends Operation { + + /** + * Bzip2Compress constructor + */ + constructor() { + super(); + + this.name = "Bzip2 Compress"; + this.module = "Compression"; + this.description = "Bzip2 is a compression library developed by Julian Seward (of GHC fame) that uses the Burrows-Wheeler algorithm. It only supports compressing single files and its compression is slow, however is more effective than Deflate (.gz & .zip)."; + this.infoURL = "https://wikipedia.org/wiki/Bzip2"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; + this.args = [ + { + name: "Block size (100s of kb)", + type: "number", + value: 9, + min: 1, + max: 9 + }, + { + name: "Work factor", + type: "number", + value: 30 + } + ]; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {File} + */ + run(input, args) { + const [blockSize, workFactor] = args; + if (input.byteLength <= 0) { + throw new OperationError("Please provide an input."); + } + if (isWorkerEnvironment()) self.sendStatusMessage("Loading Bzip2..."); + return new Promise((resolve, reject) => { + Bzip2().then(bzip2 => { + if (isWorkerEnvironment()) self.sendStatusMessage("Compressing data..."); + const inpArray = new Uint8Array(input); + const bzip2cc = bzip2.compressBZ2(inpArray, blockSize, workFactor); + if (bzip2cc.error !== 0) { + reject(new OperationError(bzip2cc.error_msg)); + } else { + const output = bzip2cc.output; + resolve(output.buffer.slice(output.byteOffset, output.byteLength + output.byteOffset)); + } + }); + }); + } + +} + +export default Bzip2Compress; diff --git a/src/core/operations/Bzip2Decompress.mjs b/src/core/operations/Bzip2Decompress.mjs index 3b357486..7fc3bd4c 100644 --- a/src/core/operations/Bzip2Decompress.mjs +++ b/src/core/operations/Bzip2Decompress.mjs @@ -1,12 +1,13 @@ /** - * @author n1474335 [n1474335@gmail.com] - * @copyright Crown Copyright 2016 + * @author Matt C [me@mitt.dev] + * @copyright Crown Copyright 2019 * @license Apache-2.0 */ -import Operation from "../Operation"; -import bzip2 from "../vendor/bzip2"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Bzip2 from "libbzip2-wasm"; +import { isWorkerEnvironment } from "../Utils.mjs"; /** * Bzip2 Decompress operation @@ -23,9 +24,15 @@ class Bzip2Decompress extends Operation { this.module = "Compression"; this.description = "Decompresses data using the Bzip2 algorithm."; this.infoURL = "https://wikipedia.org/wiki/Bzip2"; - this.inputType = "byteArray"; - this.outputType = "string"; - this.args = []; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; + this.args = [ + { + name: "Use low-memory, slower decompression algorithm", + type: "boolean", + value: false + } + ]; this.patterns = [ { "match": "^\\x42\\x5a\\x68", @@ -41,14 +48,24 @@ class Bzip2Decompress extends Operation { * @returns {string} */ run(input, args) { - const compressed = new Uint8Array(input); - - try { - const bzip2Reader = bzip2.array(compressed); - return bzip2.simple(bzip2Reader); - } catch (err) { - throw new OperationError(err); + const [small] = args; + if (input.byteLength <= 0) { + throw new OperationError("Please provide an input."); } + if (isWorkerEnvironment()) self.sendStatusMessage("Loading Bzip2..."); + return new Promise((resolve, reject) => { + Bzip2().then(bzip2 => { + if (isWorkerEnvironment()) self.sendStatusMessage("Decompressing data..."); + const inpArray = new Uint8Array(input); + const bzip2cc = bzip2.decompressBZ2(inpArray, small ? 1 : 0); + if (bzip2cc.error !== 0) { + reject(new OperationError(bzip2cc.error_msg)); + } else { + const output = bzip2cc.output; + resolve(output.buffer.slice(output.byteOffset, output.byteLength + output.byteOffset)); + } + }); + }); } } diff --git a/src/core/operations/CRC16Checksum.mjs b/src/core/operations/CRC16Checksum.mjs index 3171ad73..035ee04b 100644 --- a/src/core/operations/CRC16Checksum.mjs +++ b/src/core/operations/CRC16Checksum.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import JSCRC from "js-crc"; /** diff --git a/src/core/operations/CRC32Checksum.mjs b/src/core/operations/CRC32Checksum.mjs index 962253dc..cfe84643 100644 --- a/src/core/operations/CRC32Checksum.mjs +++ b/src/core/operations/CRC32Checksum.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import JSCRC from "js-crc"; /** diff --git a/src/core/operations/CRC8Checksum.mjs b/src/core/operations/CRC8Checksum.mjs new file mode 100644 index 00000000..193cadf9 --- /dev/null +++ b/src/core/operations/CRC8Checksum.mjs @@ -0,0 +1,157 @@ +/** + * @author mshwed [m@ttshwed.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +import { toHexFast } from "../lib/Hex.mjs"; + +/** + * CRC-8 Checksum operation + */ +class CRC8Checksum extends Operation { + + /** + * CRC8Checksum constructor + */ + constructor() { + super(); + + this.name = "CRC-8 Checksum"; + this.module = "Crypto"; + this.description = "A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data.

The CRC was invented by W. Wesley Peterson in 1961."; + this.infoURL = "https://wikipedia.org/wiki/Cyclic_redundancy_check"; + this.inputType = "ArrayBuffer"; + this.outputType = "string"; + this.args = [ + { + "name": "Algorithm", + "type": "option", + "value": [ + "CRC-8", + "CRC-8/CDMA2000", + "CRC-8/DARC", + "CRC-8/DVB-S2", + "CRC-8/EBU", + "CRC-8/I-CODE", + "CRC-8/ITU", + "CRC-8/MAXIM", + "CRC-8/ROHC", + "CRC-8/WCDMA" + ] + } + ]; + } + + /** + * Generates the pre-computed lookup table for byte division + * + * @param polynomial + */ + calculateCRC8LookupTable(polynomial) { + const crc8Table = new Uint8Array(256); + + let currentByte; + for (let i = 0; i < 256; i++) { + currentByte = i; + for (let bit = 0; bit < 8; bit++) { + if ((currentByte & 0x80) !== 0) { + currentByte <<= 1; + currentByte ^= polynomial; + } else { + currentByte <<= 1; + } + } + + crc8Table[i] = currentByte; + } + + return crc8Table; + } + + /** + * Calculates the CRC-8 Checksum from an input + * + * @param {ArrayBuffer} input + * @param {number} polynomial + * @param {number} initializationValue + * @param {boolean} inputReflection + * @param {boolean} outputReflection + * @param {number} xorOut + */ + calculateCRC8(input, polynomial, initializationValue, inputReflection, outputReflection, xorOut) { + const crcSize = 8; + const crcTable = this.calculateCRC8LookupTable(polynomial); + + let crc = initializationValue !== 0 ? initializationValue : 0; + let currentByte, position; + + input = new Uint8Array(input); + for (const inputByte of input) { + currentByte = inputReflection ? this.reverseBits(inputByte, crcSize) : inputByte; + + position = (currentByte ^ crc) & 255; + crc = crcTable[position]; + } + + crc = outputReflection ? this.reverseBits(crc, crcSize) : crc; + + if (xorOut !== 0) crc = crc ^ xorOut; + + return toHexFast(new Uint8Array([crc])); + } + + /** + * Reverse the bits for a given input byte. + * + * @param {number} input + */ + reverseBits(input, hashSize) { + let reversedByte = 0; + for (let i = hashSize - 1; i >= 0; i--) { + reversedByte |= ((input & 1) << i); + input >>= 1; + } + + return reversedByte; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const algorithm = args[0]; + + switch (algorithm) { + case "CRC-8": + return this.calculateCRC8(input, 0x7, 0x0, false, false, 0x0); + case "CRC-8/CDMA2000": + return this.calculateCRC8(input, 0x9B, 0xFF, false, false, 0x0); + case "CRC-8/DARC": + return this.calculateCRC8(input, 0x39, 0x0, true, true, 0x0); + case "CRC-8/DVB-S2": + return this.calculateCRC8(input, 0xD5, 0x0, false, false, 0x0); + case "CRC-8/EBU": + return this.calculateCRC8(input, 0x1D, 0xFF, true, true, 0x0); + case "CRC-8/I-CODE": + return this.calculateCRC8(input, 0x1D, 0xFD, false, false, 0x0); + case "CRC-8/ITU": + return this.calculateCRC8(input, 0x7, 0x0, false, false, 0x55); + case "CRC-8/MAXIM": + return this.calculateCRC8(input, 0x31, 0x0, true, true, 0x0); + case "CRC-8/ROHC": + return this.calculateCRC8(input, 0x7, 0xFF, true, true, 0x0); + case "CRC-8/WCDMA": + return this.calculateCRC8(input, 0x9B, 0x0, true, true, 0x0); + default: + throw new OperationError("Unknown checksum algorithm"); + } + } +} + +export default CRC8Checksum; diff --git a/src/core/operations/CSSBeautify.mjs b/src/core/operations/CSSBeautify.mjs index d9835550..3491b618 100644 --- a/src/core/operations/CSSBeautify.mjs +++ b/src/core/operations/CSSBeautify.mjs @@ -5,7 +5,7 @@ */ import vkbeautify from "vkbeautify"; -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * CSS Beautify operation diff --git a/src/core/operations/CSSMinify.mjs b/src/core/operations/CSSMinify.mjs index 2d489edc..3aa96bb1 100644 --- a/src/core/operations/CSSMinify.mjs +++ b/src/core/operations/CSSMinify.mjs @@ -5,7 +5,7 @@ */ import vkbeautify from "vkbeautify"; -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * CSS Minify operation diff --git a/src/core/operations/CSSSelector.mjs b/src/core/operations/CSSSelector.mjs index c26d3142..d6b8da11 100644 --- a/src/core/operations/CSSSelector.mjs +++ b/src/core/operations/CSSSelector.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; import xmldom from "xmldom"; import nwmatcher from "nwmatcher"; diff --git a/src/core/operations/CSVToJSON.mjs b/src/core/operations/CSVToJSON.mjs index d2cdb53b..ca9f1ceb 100644 --- a/src/core/operations/CSVToJSON.mjs +++ b/src/core/operations/CSVToJSON.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; /** * CSV to JSON operation diff --git a/src/core/operations/CTPH.mjs b/src/core/operations/CTPH.mjs index feb58d44..cd450dd9 100644 --- a/src/core/operations/CTPH.mjs +++ b/src/core/operations/CTPH.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import ctphjs from "ctph.js"; /** diff --git a/src/core/operations/CartesianProduct.mjs b/src/core/operations/CartesianProduct.mjs index cd32c72f..07ac575c 100644 --- a/src/core/operations/CartesianProduct.mjs +++ b/src/core/operations/CartesianProduct.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Set cartesian product operation diff --git a/src/core/operations/ChangeIPFormat.mjs b/src/core/operations/ChangeIPFormat.mjs index 56c5ffe1..fea5c561 100644 --- a/src/core/operations/ChangeIPFormat.mjs +++ b/src/core/operations/ChangeIPFormat.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import Utils from "../Utils"; -import {fromHex} from "../lib/Hex"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; +import {fromHex} from "../lib/Hex.mjs"; /** * Change IP format operation diff --git a/src/core/operations/ChiSquare.mjs b/src/core/operations/ChiSquare.mjs index 5681abc8..f78574d6 100644 --- a/src/core/operations/ChiSquare.mjs +++ b/src/core/operations/ChiSquare.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Chi Square operation diff --git a/src/core/operations/CitrixCTX1Decode.mjs b/src/core/operations/CitrixCTX1Decode.mjs index 31c20ee9..33de4a36 100644 --- a/src/core/operations/CitrixCTX1Decode.mjs +++ b/src/core/operations/CitrixCTX1Decode.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; import cptable from "../vendor/js-codepage/cptable.js"; /** @@ -23,17 +23,18 @@ class CitrixCTX1Decode extends Operation { this.module = "Encodings"; this.description = "Decodes strings in a Citrix CTX1 password format to plaintext."; this.infoURL = "https://www.reddit.com/r/AskNetsec/comments/1s3r6y/citrix_ctx1_hash_decoding/"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { + input = new Uint8Array(input); if (input.length % 4 !== 0) { throw new OperationError("Incorrect hash length"); } diff --git a/src/core/operations/CitrixCTX1Encode.mjs b/src/core/operations/CitrixCTX1Encode.mjs index add563d8..3c6e8c45 100644 --- a/src/core/operations/CitrixCTX1Encode.mjs +++ b/src/core/operations/CitrixCTX1Encode.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import cptable from "../vendor/js-codepage/cptable.js"; /** diff --git a/src/core/operations/Comment.mjs b/src/core/operations/Comment.mjs index 2c941089..af74cf48 100644 --- a/src/core/operations/Comment.mjs +++ b/src/core/operations/Comment.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Comment operation diff --git a/src/core/operations/CompareCTPHHashes.mjs b/src/core/operations/CompareCTPHHashes.mjs index b8cd4c55..82156d27 100644 --- a/src/core/operations/CompareCTPHHashes.mjs +++ b/src/core/operations/CompareCTPHHashes.mjs @@ -4,11 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {HASH_DELIM_OPTIONS} from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {HASH_DELIM_OPTIONS} from "../lib/Delim.mjs"; import ctphjs from "ctph.js"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * Compare CTPH hashes operation diff --git a/src/core/operations/CompareSSDEEPHashes.mjs b/src/core/operations/CompareSSDEEPHashes.mjs index 46aa8dac..f443d269 100644 --- a/src/core/operations/CompareSSDEEPHashes.mjs +++ b/src/core/operations/CompareSSDEEPHashes.mjs @@ -4,11 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {HASH_DELIM_OPTIONS} from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {HASH_DELIM_OPTIONS} from "../lib/Delim.mjs"; import ssdeepjs from "ssdeep.js"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * Compare SSDEEP hashes operation diff --git a/src/core/operations/ConditionalJump.mjs b/src/core/operations/ConditionalJump.mjs index d102ea72..ca8eea30 100644 --- a/src/core/operations/ConditionalJump.mjs +++ b/src/core/operations/ConditionalJump.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Dish from "../Dish"; -import { getLabelIndex } from "../lib/FlowControl"; +import Operation from "../Operation.mjs"; +import Dish from "../Dish.mjs"; +import { getLabelIndex } from "../lib/FlowControl.mjs"; /** * Conditional Jump operation diff --git a/src/core/operations/ContainImage.mjs b/src/core/operations/ContainImage.mjs index c6df81ef..7da50ee6 100644 --- a/src/core/operations/ContainImage.mjs +++ b/src/core/operations/ContainImage.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; import { toBase64 } from "../lib/Base64.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; import jimp from "jimp"; /** @@ -25,8 +26,8 @@ class ContainImage extends Operation { this.module = "Image"; this.description = "Scales an image to the specified width and height, maintaining the aspect ratio. The image may be letterboxed."; this.infoURL = ""; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -72,17 +73,22 @@ class ContainImage extends Operation { "Bezier" ], defaultIndex: 1 + }, + { + name: "Opaque background", + type: "boolean", + value: true } ]; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ async run(input, args) { - const [width, height, hAlign, vAlign, alg] = args; + const [width, height, hAlign, vAlign, alg, opaqueBg] = args; const resizeMap = { "Nearest Neighbour": jimp.RESIZE_NEAREST_NEIGHBOR, @@ -101,22 +107,34 @@ class ContainImage extends Operation { "Bottom": jimp.VERTICAL_ALIGN_BOTTOM }; - if (!isImage(input)) { + if (!isImage(new Uint8Array(input))) { throw new OperationError("Invalid file type."); } let image; try { - image = await jimp.read(Buffer.from(input)); + image = await jimp.read(input); } catch (err) { throw new OperationError(`Error loading image. (${err})`); } try { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Containing image..."); image.contain(width, height, alignMap[hAlign] | alignMap[vAlign], resizeMap[alg]); - const imageBuffer = await image.getBufferAsync(jimp.AUTO); - return [...imageBuffer]; + + if (opaqueBg) { + const newImage = await jimp.read(width, height, 0x000000FF); + newImage.blit(image, 0, 0); + image = newImage; + } + + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; } catch (err) { throw new OperationError(`Error containing image. (${err})`); } @@ -124,18 +142,19 @@ class ContainImage extends Operation { /** * Displays the contained image using HTML for web apps - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/ConvertArea.mjs b/src/core/operations/ConvertArea.mjs index 1c4a80c3..4cce31b1 100644 --- a/src/core/operations/ConvertArea.mjs +++ b/src/core/operations/ConvertArea.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Convert area operation diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index 87e44bf2..f1e1b20f 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {FORMATS, convertCoordinates} from "../lib/ConvertCoordinates"; +import Operation from "../Operation.mjs"; +import {FORMATS, convertCoordinates} from "../lib/ConvertCoordinates.mjs"; /** * Convert co-ordinate format operation diff --git a/src/core/operations/ConvertDataUnits.mjs b/src/core/operations/ConvertDataUnits.mjs index c35bd6fe..baed9004 100644 --- a/src/core/operations/ConvertDataUnits.mjs +++ b/src/core/operations/ConvertDataUnits.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Convert data units operation diff --git a/src/core/operations/ConvertDistance.mjs b/src/core/operations/ConvertDistance.mjs index 1151abc6..1a5fc8af 100644 --- a/src/core/operations/ConvertDistance.mjs +++ b/src/core/operations/ConvertDistance.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Convert distance operation diff --git a/src/core/operations/ConvertImageFormat.mjs b/src/core/operations/ConvertImageFormat.mjs new file mode 100644 index 00000000..b0cc4982 --- /dev/null +++ b/src/core/operations/ConvertImageFormat.mjs @@ -0,0 +1,143 @@ +/** + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; +import jimp from "jimp"; + +/** + * Convert Image Format operation + */ +class ConvertImageFormat extends Operation { + + /** + * ConvertImageFormat constructor + */ + constructor() { + super(); + + this.name = "Convert Image Format"; + this.module = "Image"; + this.description = "Converts an image between different formats. Supported formats:
  • Joint Photographic Experts Group (JPEG)
  • Portable Network Graphics (PNG)
  • Bitmap (BMP)
  • Tagged Image File Format (TIFF)

Note: GIF files are supported for input, but cannot be outputted."; + this.infoURL = "https://wikipedia.org/wiki/Image_file_formats"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; + this.presentType = "html"; + this.args = [ + { + name: "Output Format", + type: "option", + value: [ + "JPEG", + "PNG", + "BMP", + "TIFF" + ] + }, + { + name: "JPEG Quality", + type: "number", + value: 80, + min: 1, + max: 100 + }, + { + name: "PNG Filter Type", + type: "option", + value: [ + "Auto", + "None", + "Sub", + "Up", + "Average", + "Paeth" + ] + }, + { + name: "PNG Deflate Level", + type: "number", + value: 9, + min: 0, + max: 9 + } + ]; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {byteArray} + */ + async run(input, args) { + const [format, jpegQuality, pngFilterType, pngDeflateLevel] = args; + const formatMap = { + "JPEG": jimp.MIME_JPEG, + "PNG": jimp.MIME_PNG, + "BMP": jimp.MIME_BMP, + "TIFF": jimp.MIME_TIFF + }; + + const pngFilterMap = { + "Auto": jimp.PNG_FILTER_AUTO, + "None": jimp.PNG_FILTER_NONE, + "Sub": jimp.PNG_FILTER_SUB, + "Up": jimp.PNG_FILTER_UP, + "Average": jimp.PNG_FILTER_AVERAGE, + "Paeth": jimp.PNG_FILTER_PATH // Incorrect spelling in Jimp library + }; + + const mime = formatMap[format]; + + if (!isImage(new Uint8Array(input))) { + throw new OperationError("Invalid file format."); + } + let image; + try { + image = await jimp.read(input); + } catch (err) { + throw new OperationError(`Error opening image file. (${err})`); + } + try { + switch (format) { + case "JPEG": + image.quality(jpegQuality); + break; + case "PNG": + image.filterType(pngFilterMap[pngFilterType]); + image.deflateLevel(pngDeflateLevel); + break; + } + + const imageBuffer = await image.getBufferAsync(mime); + return imageBuffer.buffer; + } catch (err) { + throw new OperationError(`Error converting image format. (${err})`); + } + } + + /** + * Displays the converted image using HTML for web apps + * + * @param {ArrayBuffer} data + * @returns {html} + */ + present(data) { + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); + + const type = isImage(dataArray); + if (!type) { + throw new OperationError("Invalid file type."); + } + + return ``; + } + +} + +export default ConvertImageFormat; diff --git a/src/core/operations/ConvertMass.mjs b/src/core/operations/ConvertMass.mjs index 3373aa57..712884ed 100644 --- a/src/core/operations/ConvertMass.mjs +++ b/src/core/operations/ConvertMass.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Convert mass operation diff --git a/src/core/operations/ConvertSpeed.mjs b/src/core/operations/ConvertSpeed.mjs index ec92ad95..7fc6718d 100644 --- a/src/core/operations/ConvertSpeed.mjs +++ b/src/core/operations/ConvertSpeed.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Convert speed operation diff --git a/src/core/operations/CountOccurrences.mjs b/src/core/operations/CountOccurrences.mjs index 5027a0f0..36ee33a8 100644 --- a/src/core/operations/CountOccurrences.mjs +++ b/src/core/operations/CountOccurrences.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * Count occurrences operation diff --git a/src/core/operations/CoverImage.mjs b/src/core/operations/CoverImage.mjs index 07466308..f216b1f9 100644 --- a/src/core/operations/CoverImage.mjs +++ b/src/core/operations/CoverImage.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; import { toBase64 } from "../lib/Base64.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; import jimp from "jimp"; /** @@ -25,8 +26,8 @@ class CoverImage extends Operation { this.module = "Image"; this.description = "Scales the image to the given width and height, keeping the aspect ratio. The image may be clipped."; this.infoURL = ""; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -77,7 +78,7 @@ class CoverImage extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ @@ -101,22 +102,27 @@ class CoverImage extends Operation { "Bottom": jimp.VERTICAL_ALIGN_BOTTOM }; - if (!isImage(input)) { + if (!isImage(new Uint8Array(input))) { throw new OperationError("Invalid file type."); } let image; try { - image = await jimp.read(Buffer.from(input)); + image = await jimp.read(input); } catch (err) { throw new OperationError(`Error loading image. (${err})`); } try { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Covering image..."); image.cover(width, height, alignMap[hAlign] | alignMap[vAlign], resizeMap[alg]); - const imageBuffer = await image.getBufferAsync(jimp.AUTO); - return [...imageBuffer]; + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; } catch (err) { throw new OperationError(`Error covering image. (${err})`); } @@ -124,18 +130,19 @@ class CoverImage extends Operation { /** * Displays the covered image using HTML for web apps - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/CropImage.mjs b/src/core/operations/CropImage.mjs index efbf29f9..e74a1617 100644 --- a/src/core/operations/CropImage.mjs +++ b/src/core/operations/CropImage.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; import { toBase64 } from "../lib/Base64.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; import jimp from "jimp"; /** @@ -25,8 +26,8 @@ class CropImage extends Operation { this.module = "Image"; this.description = "Crops an image to the specified region, or automatically crops edges.

Autocrop
Automatically crops same-colour borders from the image.

Autocrop tolerance
A percentage value for the tolerance of colour difference between pixels.

Only autocrop frames
Only crop real frames (all sides must have the same border)

Symmetric autocrop
Force autocrop to be symmetric (top/bottom and left/right are cropped by the same amount)

Autocrop keep border
The number of pixels of border to leave around the image."; this.infoURL = "https://wikipedia.org/wiki/Cropping_(image)"; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -86,24 +87,24 @@ class CropImage extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ async run(input, args) { const [xPos, yPos, width, height, autocrop, autoTolerance, autoFrames, autoSymmetric, autoBorder] = args; - if (!isImage(input)) { + if (!isImage(new Uint8Array(input))) { throw new OperationError("Invalid file type."); } let image; try { - image = await jimp.read(Buffer.from(input)); + image = await jimp.read(input); } catch (err) { throw new OperationError(`Error loading image. (${err})`); } try { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Cropping image..."); if (autocrop) { image.autocrop({ @@ -116,8 +117,13 @@ class CropImage extends Operation { image.crop(xPos, yPos, width, height); } - const imageBuffer = await image.getBufferAsync(jimp.AUTO); - return [...imageBuffer]; + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; } catch (err) { throw new OperationError(`Error cropping image. (${err})`); } @@ -125,18 +131,19 @@ class CropImage extends Operation { /** * Displays the cropped image using HTML for web apps - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/DESDecrypt.mjs b/src/core/operations/DESDecrypt.mjs index 620256c7..6725160a 100644 --- a/src/core/operations/DESDecrypt.mjs +++ b/src/core/operations/DESDecrypt.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; import forge from "node-forge/dist/forge.min.js"; /** diff --git a/src/core/operations/DESEncrypt.mjs b/src/core/operations/DESEncrypt.mjs index 774be674..5ea5f5fc 100644 --- a/src/core/operations/DESEncrypt.mjs +++ b/src/core/operations/DESEncrypt.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; import forge from "node-forge/dist/forge.min.js"; /** diff --git a/src/core/operations/DNSOverHTTPS.mjs b/src/core/operations/DNSOverHTTPS.mjs index b56feb6a..b6e98250 100644 --- a/src/core/operations/DNSOverHTTPS.mjs +++ b/src/core/operations/DNSOverHTTPS.mjs @@ -3,8 +3,8 @@ * @copyright Crown Copyright 2019 * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * DNS over HTTPS operation diff --git a/src/core/operations/DechunkHTTPResponse.mjs b/src/core/operations/DechunkHTTPResponse.mjs index 61af035e..6a4c3813 100644 --- a/src/core/operations/DechunkHTTPResponse.mjs +++ b/src/core/operations/DechunkHTTPResponse.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Dechunk HTTP response operation diff --git a/src/core/operations/DecodeNetBIOSName.mjs b/src/core/operations/DecodeNetBIOSName.mjs index bbfe2b6e..f4d89f4a 100644 --- a/src/core/operations/DecodeNetBIOSName.mjs +++ b/src/core/operations/DecodeNetBIOSName.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Decode NetBIOS Name operation diff --git a/src/core/operations/DecodeText.mjs b/src/core/operations/DecodeText.mjs index a5d60706..489e40d3 100644 --- a/src/core/operations/DecodeText.mjs +++ b/src/core/operations/DecodeText.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import cptable from "../vendor/js-codepage/cptable.js"; -import {IO_FORMAT} from "../lib/ChrEnc"; +import {IO_FORMAT} from "../lib/ChrEnc.mjs"; /** * Decode text operation @@ -30,7 +30,7 @@ class DecodeText extends Operation { "", ].join("\n"); this.infoURL = "https://wikipedia.org/wiki/Character_encoding"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { @@ -42,13 +42,13 @@ class DecodeText extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { const format = IO_FORMAT[args[0]]; - return cptable.utils.decode(format, input); + return cptable.utils.decode(format, new Uint8Array(input)); } } diff --git a/src/core/operations/DefangURL.mjs b/src/core/operations/DefangURL.mjs index 57d4298e..aa783c32 100644 --- a/src/core/operations/DefangURL.mjs +++ b/src/core/operations/DefangURL.mjs @@ -5,8 +5,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {URL_REGEX, DOMAIN_REGEX} from "../lib/Extract"; +import Operation from "../Operation.mjs"; +import {URL_REGEX, DOMAIN_REGEX} from "../lib/Extract.mjs"; /** * DefangURL operation diff --git a/src/core/operations/DeriveEVPKey.mjs b/src/core/operations/DeriveEVPKey.mjs index a14d2249..b729d6ea 100644 --- a/src/core/operations/DeriveEVPKey.mjs +++ b/src/core/operations/DeriveEVPKey.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; import CryptoJS from "crypto-js"; /** diff --git a/src/core/operations/DerivePBKDF2Key.mjs b/src/core/operations/DerivePBKDF2Key.mjs index 53fedb4d..e4b9c406 100644 --- a/src/core/operations/DerivePBKDF2Key.mjs +++ b/src/core/operations/DerivePBKDF2Key.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; import forge from "node-forge/dist/forge.min.js"; /** diff --git a/src/core/operations/DetectFileType.mjs b/src/core/operations/DetectFileType.mjs index 2321cee8..7ddef0f9 100644 --- a/src/core/operations/DetectFileType.mjs +++ b/src/core/operations/DetectFileType.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {detectFileType} from "../lib/FileType"; -import {FILE_SIGNATURES} from "../lib/FileSignatures"; +import Operation from "../Operation.mjs"; +import {detectFileType} from "../lib/FileType.mjs"; +import {FILE_SIGNATURES} from "../lib/FileSignatures.mjs"; /** * Detect File Type operation @@ -21,7 +21,12 @@ class DetectFileType extends Operation { this.name = "Detect File Type"; this.module = "Default"; - this.description = "Attempts to guess the MIME (Multipurpose Internet Mail Extensions) type of the data based on 'magic bytes'.

Currently supports the following file types: 7z, amr, avi, bmp, bz2, class, cr2, crx, dex, dmg, doc, elf, eot, epub, exe, flac, flv, gif, gz, ico, iso, jpg, jxr, m4a, m4v, mid, mkv, mov, mp3, mp4, mpg, ogg, otf, pdf, png, ppt, ps, psd, rar, rtf, sqlite, swf, tar, tar.z, tif, ttf, utf8, vmdk, wav, webm, webp, wmv, woff, woff2, xls, xz, zip."; + this.description = "Attempts to guess the MIME (Multipurpose Internet Mail Extensions) type of the data based on 'magic bytes'.

Currently supports the following file types: " + + Object.keys(FILE_SIGNATURES).map(cat => + FILE_SIGNATURES[cat].map(sig => + sig.extension.split(",")[0] + ).join(", ") + ).join(", ") + "."; this.infoURL = "https://wikipedia.org/wiki/List_of_file_signatures"; this.inputType = "ArrayBuffer"; this.outputType = "string"; @@ -52,18 +57,19 @@ class DetectFileType extends Operation { if (!types.length) { return "Unknown file type. Have you tried checking the entropy of this data to determine whether it might be encrypted or compressed?"; } else { - let output = ""; - - types.forEach(type => { - output += "File extension: " + type.extension + "\n" + - "MIME type: " + type.mime + "\n"; + const results = types.map(type => { + let output = `File type: ${type.name} +Extension: ${type.extension} +MIME type: ${type.mime}\n`; if (type.description && type.description.length) { - output += "\nDescription: " + type.description + "\n"; + output += `Description: ${type.description}\n`; } + + return output; }); - return output; + return results.join("\n"); } } diff --git a/src/core/operations/Diff.mjs b/src/core/operations/Diff.mjs index 9e094e99..7adea178 100644 --- a/src/core/operations/Diff.mjs +++ b/src/core/operations/Diff.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; import * as JsDiff from "diff"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * Diff operation diff --git a/src/core/operations/DisassembleX86.mjs b/src/core/operations/DisassembleX86.mjs index b25dcb48..bdaf348a 100644 --- a/src/core/operations/DisassembleX86.mjs +++ b/src/core/operations/DisassembleX86.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import * as disassemble from "../vendor/DisassembleX86-64"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import * as disassemble from "../vendor/DisassembleX86-64.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Disassemble x86 operation diff --git a/src/core/operations/DitherImage.mjs b/src/core/operations/DitherImage.mjs index 13011837..469711e7 100644 --- a/src/core/operations/DitherImage.mjs +++ b/src/core/operations/DitherImage.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; -import { toBase64 } from "../lib/Base64"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; import jimp from "jimp"; /** @@ -25,34 +26,40 @@ class DitherImage extends Operation { this.module = "Image"; this.description = "Apply a dither effect to an image."; this.infoURL = "https://wikipedia.org/wiki/Dither"; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ async run(input, args) { - if (!isImage(input)) { + if (!isImage(new Uint8Array(input))) { throw new OperationError("Invalid file type."); } let image; try { - image = await jimp.read(Buffer.from(input)); + image = await jimp.read(input); } catch (err) { throw new OperationError(`Error loading image. (${err})`); } try { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Applying dither to image..."); image.dither565(); - const imageBuffer = await image.getBufferAsync(jimp.AUTO); - return [...imageBuffer]; + + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; } catch (err) { throw new OperationError(`Error applying dither to image. (${err})`); } @@ -60,18 +67,19 @@ class DitherImage extends Operation { /** * Displays the dithered image using HTML for web apps - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/Divide.mjs b/src/core/operations/Divide.mjs index 5ca88be0..108c175b 100644 --- a/src/core/operations/Divide.mjs +++ b/src/core/operations/Divide.mjs @@ -6,9 +6,9 @@ */ import BigNumber from "bignumber.js"; -import Operation from "../Operation"; -import { div, createNumArray } from "../lib/Arithmetic"; -import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import { div, createNumArray } from "../lib/Arithmetic.mjs"; +import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim.mjs"; /** diff --git a/src/core/operations/DropBytes.mjs b/src/core/operations/DropBytes.mjs index 80492c34..9ea105f8 100644 --- a/src/core/operations/DropBytes.mjs +++ b/src/core/operations/DropBytes.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Drop bytes operation diff --git a/src/core/operations/EncodeNetBIOSName.mjs b/src/core/operations/EncodeNetBIOSName.mjs index 9352ed4f..bcc9a11d 100644 --- a/src/core/operations/EncodeNetBIOSName.mjs +++ b/src/core/operations/EncodeNetBIOSName.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Encode NetBIOS Name operation diff --git a/src/core/operations/EncodeText.mjs b/src/core/operations/EncodeText.mjs index dd3241a2..8dd4d503 100644 --- a/src/core/operations/EncodeText.mjs +++ b/src/core/operations/EncodeText.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import cptable from "../vendor/js-codepage/cptable.js"; -import {IO_FORMAT} from "../lib/ChrEnc"; +import {IO_FORMAT} from "../lib/ChrEnc.mjs"; /** * Encode text operation @@ -31,7 +31,7 @@ class EncodeText extends Operation { ].join("\n"); this.infoURL = "https://wikipedia.org/wiki/Character_encoding"; this.inputType = "string"; - this.outputType = "byteArray"; + this.outputType = "ArrayBuffer"; this.args = [ { "name": "Encoding", @@ -44,13 +44,12 @@ class EncodeText extends Operation { /** * @param {string} input * @param {Object[]} args - * @returns {byteArray} + * @returns {ArrayBuffer} */ run(input, args) { const format = IO_FORMAT[args[0]]; - let encoded = cptable.utils.encode(format, input); - encoded = Array.from(encoded); - return encoded; + const encoded = cptable.utils.encode(format, input); + return new Uint8Array(encoded).buffer; } } diff --git a/src/core/operations/Enigma.mjs b/src/core/operations/Enigma.mjs index 542e8281..3e8b7dcb 100644 --- a/src/core/operations/Enigma.mjs +++ b/src/core/operations/Enigma.mjs @@ -6,9 +6,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import {ROTORS, LETTERS, ROTORS_FOURTH, REFLECTORS, Rotor, Reflector, Plugboard, EnigmaMachine} from "../lib/Enigma"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {ROTORS, LETTERS, ROTORS_FOURTH, REFLECTORS, Rotor, Reflector, Plugboard, EnigmaMachine} from "../lib/Enigma.mjs"; /** * Enigma operation diff --git a/src/core/operations/Entropy.mjs b/src/core/operations/Entropy.mjs index 868178fc..3e93cc57 100644 --- a/src/core/operations/Entropy.mjs +++ b/src/core/operations/Entropy.mjs @@ -4,8 +4,13 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import * as d3temp from "d3"; +import * as nodomtemp from "nodom"; + +import Operation from "../Operation.mjs"; + +const d3 = d3temp.default ? d3temp.default : d3temp; +const nodom = nodomtemp.default ? nodomtemp.default: nodomtemp; /** * Entropy operation @@ -19,30 +24,45 @@ class Entropy extends Operation { super(); this.name = "Entropy"; - this.module = "Default"; + this.module = "Charts"; this.description = "Shannon Entropy, in the context of information theory, is a measure of the rate at which information is produced by a source of data. It can be used, in a broad sense, to detect whether data is likely to be structured or unstructured. 8 is the maximum, representing highly unstructured, 'random' data. English language text usually falls somewhere between 3.5 and 5. Properly encrypted or compressed data should have an entropy of over 7.5."; this.infoURL = "https://wikipedia.org/wiki/Entropy_(information_theory)"; - this.inputType = "byteArray"; - this.outputType = "number"; + this.inputType = "ArrayBuffer"; + this.outputType = "json"; this.presentType = "html"; - this.args = []; + this.args = [ + { + "name": "Visualisation", + "type": "option", + "value": ["Shannon scale", "Histogram (Bar)", "Histogram (Line)", "Curve", "Image"] + } + ]; } /** - * @param {byteArray} input - * @param {Object[]} args + * Calculates the frequency of bytes in the input. + * + * @param {Uint8Array} input * @returns {number} */ - run(input, args) { + calculateShannonEntropy(input) { const prob = [], - uniques = input.unique(), - str = Utils.byteArrayToChars(input); - let i; + occurrences = new Array(256).fill(0); - for (i = 0; i < uniques.length; i++) { - prob.push(str.count(Utils.chr(uniques[i])) / input.length); + // Count occurrences of each byte in the input + let i; + for (i = 0; i < input.length; i++) { + occurrences[input[i]]++; } + // Store probability list + for (i = 0; i < occurrences.length; i++) { + if (occurrences[i] > 0) { + prob.push(occurrences[i] / input.length); + } + } + + // Calculate Shannon entropy let entropy = 0, p; @@ -54,44 +74,357 @@ class Entropy extends Operation { return -entropy; } + /** + * Calculates the scanning entropy of the input + * + * @param {Uint8Array} inputBytes + * @returns {Object} + */ + calculateScanningEntropy(inputBytes) { + const entropyData = []; + const binWidth = inputBytes.length < 256 ? 8 : 256; + + for (let bytePos = 0; bytePos < inputBytes.length; bytePos += binWidth) { + const block = inputBytes.slice(bytePos, bytePos+binWidth); + entropyData.push(this.calculateShannonEntropy(block)); + } + + return { entropyData, binWidth }; + } + + /** + * Calculates the frequency of bytes in the input. + * + * @param {object} svg + * @param {function} xScale + * @param {function} yScale + * @param {integer} svgHeight + * @param {integer} svgWidth + * @param {object} margins + * @param {string} xTitle + * @param {string} yTitle + */ + createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, title, xTitle, yTitle) { + // Axes + const yAxis = d3.axisLeft() + .scale(yScale); + + const xAxis = d3.axisBottom() + .scale(xScale); + + svg.append("g") + .attr("transform", `translate(0, ${svgHeight - margins.bottom})`) + .call(xAxis); + + svg.append("g") + .attr("transform", `translate(${margins.left},0)`) + .call(yAxis); + + // Axes labels + svg.append("text") + .attr("transform", "rotate(-90)") + .attr("y", 0 - margins.left) + .attr("x", 0 - (svgHeight / 2)) + .attr("dy", "1em") + .style("text-anchor", "middle") + .text(yTitle); + + svg.append("text") + .attr("transform", `translate(${svgWidth / 2}, ${svgHeight - margins.bottom + 40})`) + .style("text-anchor", "middle") + .text(xTitle); + + // Add title + svg.append("text") + .attr("transform", `translate(${svgWidth / 2}, ${margins.top - 10})`) + .style("text-anchor", "middle") + .text(title); + } + + /** + * Calculates the frequency of bytes in the input. + * + * @param {Uint8Array} inputBytes + * @returns {number[]} + */ + calculateByteFrequency(inputBytes) { + const freq = new Array(256).fill(0); + if (inputBytes.length === 0) return freq; + + // Count occurrences of each byte in the input + let i; + for (i = 0; i < inputBytes.length; i++) { + freq[inputBytes[i]]++; + } + + for (i = 0; i < freq.length; i++) { + freq[i] = freq[i] / inputBytes.length; + } + + return freq; + } + + /** + * Calculates the frequency of bytes in the input. + * + * @param {number[]} byteFrequency + * @returns {HTML} + */ + createByteFrequencyLineHistogram(byteFrequency) { + const margins = { top: 30, right: 20, bottom: 50, left: 30 }; + + const svgWidth = 500, + svgHeight = 500; + + const document = new nodom.Document(); + let svg = document.createElement("svg"); + + svg = d3.select(svg) + .attr("width", "100%") + .attr("height", "100%") + .attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`); + + const yScale = d3.scaleLinear() + .domain([0, d3.max(byteFrequency, d => d)]) + .range([svgHeight - margins.bottom, margins.top]); + + const xScale = d3.scaleLinear() + .domain([0, byteFrequency.length - 1]) + .range([margins.left, svgWidth - margins.right]); + + const line = d3.line() + .x((_, i) => xScale(i)) + .y(d => yScale(d)) + .curve(d3.curveMonotoneX); + + svg.append("path") + .datum(byteFrequency) + .attr("fill", "none") + .attr("stroke", "steelblue") + .attr("d", line); + + this.createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, "", "Byte", "Byte Frequency"); + + return svg._groups[0][0].outerHTML; + } + + /** + * Creates a byte frequency histogram + * + * @param {number[]} byteFrequency + * @returns {HTML} + */ + createByteFrequencyBarHistogram(byteFrequency) { + const margins = { top: 30, right: 20, bottom: 50, left: 30 }; + + const svgWidth = 500, + svgHeight = 500, + binWidth = 1; + + const document = new nodom.Document(); + let svg = document.createElement("svg"); + svg = d3.select(svg) + .attr("width", "100%") + .attr("height", "100%") + .attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`); + + const yExtent = d3.extent(byteFrequency, d => d); + const yScale = d3.scaleLinear() + .domain(yExtent) + .range([svgHeight - margins.bottom, margins.top]); + + const xScale = d3.scaleLinear() + .domain([0, byteFrequency.length - 1]) + .range([margins.left - binWidth, svgWidth - margins.right]); + + svg.selectAll("rect") + .data(byteFrequency) + .enter().append("rect") + .attr("x", (_, i) => xScale(i) + binWidth) + .attr("y", dataPoint => yScale(dataPoint)) + .attr("width", binWidth) + .attr("height", dataPoint => yScale(yExtent[0]) - yScale(dataPoint)) + .attr("fill", "blue"); + + this.createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, "", "Byte", "Byte Frequency"); + + return svg._groups[0][0].outerHTML; + } + + /** + * Creates a byte frequency histogram + * + * @param {number[]} entropyData + * @returns {HTML} + */ + createEntropyCurve(entropyData) { + const margins = { top: 30, right: 20, bottom: 50, left: 30 }; + + const svgWidth = 500, + svgHeight = 500; + + const document = new nodom.Document(); + let svg = document.createElement("svg"); + svg = d3.select(svg) + .attr("width", "100%") + .attr("height", "100%") + .attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`); + + const yScale = d3.scaleLinear() + .domain([0, d3.max(entropyData, d => d)]) + .range([svgHeight - margins.bottom, margins.top]); + + const xScale = d3.scaleLinear() + .domain([0, entropyData.length]) + .range([margins.left, svgWidth - margins.right]); + + const line = d3.line() + .x((_, i) => xScale(i)) + .y(d => yScale(d)) + .curve(d3.curveMonotoneX); + + if (entropyData.length > 0) { + svg.append("path") + .datum(entropyData) + .attr("d", line); + + svg.selectAll("path").attr("fill", "none").attr("stroke", "steelblue"); + } + + this.createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, "Scanning Entropy", "Block", "Entropy"); + + return svg._groups[0][0].outerHTML; + } + + /** + * Creates an image representation of the entropy + * + * @param {number[]} entropyData + * @returns {HTML} + */ + createEntropyImage(entropyData) { + const svgHeight = 100, + svgWidth = 100, + cellSize = 1, + nodes = []; + + for (let i = 0; i < entropyData.length; i++) { + nodes.push({ + x: i % svgWidth, + y: Math.floor(i / svgWidth), + entropy: entropyData[i] + }); + } + + const document = new nodom.Document(); + let svg = document.createElement("svg"); + svg = d3.select(svg) + .attr("width", "100%") + .attr("height", "100%") + .attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`); + + const greyScale = d3.scaleLinear() + .domain([0, d3.max(entropyData, d => d)]) + .range(["#000000", "#FFFFFF"]) + .interpolate(d3.interpolateRgb); + + svg + .selectAll("rect") + .data(nodes) + .enter().append("rect") + .attr("x", d => d.x * cellSize) + .attr("y", d => d.y * cellSize) + .attr("width", cellSize) + .attr("height", cellSize) + .style("fill", d => greyScale(d.entropy)); + + return svg._groups[0][0].outerHTML; + } + /** * Displays the entropy as a scale bar for web apps. * * @param {number} entropy - * @returns {html} + * @returns {HTML} */ - present(entropy) { + createShannonEntropyVisualization(entropy) { return `Shannon entropy: ${entropy} -

-- 0 represents no randomness (i.e. all the bytes in the data have the same value) whereas 8, the maximum, represents a completely random string. -- Standard English text usually falls somewhere between 3.5 and 5. -- Properly encrypted or compressed data of a reasonable length should have an entropy of over 7.5. +

+ - 0 represents no randomness (i.e. all the bytes in the data have the same value) whereas 8, the maximum, represents a completely random string. + - Standard English text usually falls somewhere between 3.5 and 5. + - Properly encrypted or compressed data of a reasonable length should have an entropy of over 7.5. -The following results show the entropy of chunks of the input data. Chunks with particularly high entropy could suggest encrypted or compressed sections. + The following results show the entropy of chunks of the input data. Chunks with particularly high entropy could suggest encrypted or compressed sections. -
`; + CanvasComponents.drawScaleBar(canvas, entropy, 8, [ + { + label: "English text", + min: 3.5, + max: 5 + },{ + label: "Encrypted/compressed", + min: 7.5, + max: 8 + } + ]); + `; } + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {json} + */ + run(input, args) { + const visualizationType = args[0]; + input = new Uint8Array(input); + + switch (visualizationType) { + case "Histogram (Bar)": + case "Histogram (Line)": + return this.calculateByteFrequency(input); + case "Curve": + case "Image": + return this.calculateScanningEntropy(input).entropyData; + case "Shannon scale": + default: + return this.calculateShannonEntropy(input); + } + } + + /** + * Displays the entropy in a visualisation for web apps. + * + * @param {json} entropyData + * @param {Object[]} args + * @returns {html} + */ + present(entropyData, args) { + const visualizationType = args[0]; + + switch (visualizationType) { + case "Histogram (Bar)": + return this.createByteFrequencyBarHistogram(entropyData); + case "Histogram (Line)": + return this.createByteFrequencyLineHistogram(entropyData); + case "Curve": + return this.createEntropyCurve(entropyData); + case "Image": + return this.createEntropyImage(entropyData); + case "Shannon scale": + default: + return this.createShannonEntropyVisualization(entropyData); + } + } } export default Entropy; diff --git a/src/core/operations/EscapeString.mjs b/src/core/operations/EscapeString.mjs index 2d945e24..3ddea181 100644 --- a/src/core/operations/EscapeString.mjs +++ b/src/core/operations/EscapeString.mjs @@ -5,7 +5,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import jsesc from "jsesc"; /** diff --git a/src/core/operations/EscapeUnicodeCharacters.mjs b/src/core/operations/EscapeUnicodeCharacters.mjs index 9bbf4f69..ad5ef3ea 100644 --- a/src/core/operations/EscapeUnicodeCharacters.mjs +++ b/src/core/operations/EscapeUnicodeCharacters.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Escape Unicode Characters operation diff --git a/src/core/operations/ExpandAlphabetRange.mjs b/src/core/operations/ExpandAlphabetRange.mjs index 761a7a3b..62afeb65 100644 --- a/src/core/operations/ExpandAlphabetRange.mjs +++ b/src/core/operations/ExpandAlphabetRange.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * Expand alphabet range operation diff --git a/src/core/operations/ExtractDates.mjs b/src/core/operations/ExtractDates.mjs index 530db194..dfe93c88 100644 --- a/src/core/operations/ExtractDates.mjs +++ b/src/core/operations/ExtractDates.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { search } from "../lib/Extract"; +import Operation from "../Operation.mjs"; +import { search } from "../lib/Extract.mjs"; /** * Extract dates operation diff --git a/src/core/operations/ExtractDomains.mjs b/src/core/operations/ExtractDomains.mjs index ddd7ca97..ed1bbdb6 100644 --- a/src/core/operations/ExtractDomains.mjs +++ b/src/core/operations/ExtractDomains.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { search, DOMAIN_REGEX } from "../lib/Extract"; +import Operation from "../Operation.mjs"; +import { search, DOMAIN_REGEX } from "../lib/Extract.mjs"; /** * Extract domains operation diff --git a/src/core/operations/ExtractEXIF.mjs b/src/core/operations/ExtractEXIF.mjs index 7a96d24a..7edc8b80 100644 --- a/src/core/operations/ExtractEXIF.mjs +++ b/src/core/operations/ExtractEXIF.mjs @@ -5,8 +5,8 @@ */ import ExifParser from "exif-parser"; -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Extract EXIF operation diff --git a/src/core/operations/ExtractEmailAddresses.mjs b/src/core/operations/ExtractEmailAddresses.mjs index 54ccf95b..13bc023b 100644 --- a/src/core/operations/ExtractEmailAddresses.mjs +++ b/src/core/operations/ExtractEmailAddresses.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { search } from "../lib/Extract"; +import Operation from "../Operation.mjs"; +import { search } from "../lib/Extract.mjs"; /** * Extract email addresses operation diff --git a/src/core/operations/ExtractFilePaths.mjs b/src/core/operations/ExtractFilePaths.mjs index 4b268192..04d3f73e 100644 --- a/src/core/operations/ExtractFilePaths.mjs +++ b/src/core/operations/ExtractFilePaths.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { search } from "../lib/Extract"; +import Operation from "../Operation.mjs"; +import { search } from "../lib/Extract.mjs"; /** * Extract file paths operation diff --git a/src/core/operations/ExtractFiles.mjs b/src/core/operations/ExtractFiles.mjs index c71f0148..dd146838 100644 --- a/src/core/operations/ExtractFiles.mjs +++ b/src/core/operations/ExtractFiles.mjs @@ -4,11 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import Utils from "../Utils"; -import {scanForFileTypes, extractFile} from "../lib/FileType"; -import {FILE_SIGNATURES} from "../lib/FileSignatures"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; +import {scanForFileTypes, extractFile} from "../lib/FileType.mjs"; +import {FILE_SIGNATURES} from "../lib/FileSignatures.mjs"; /** * Extract Files operation diff --git a/src/core/operations/ExtractIPAddresses.mjs b/src/core/operations/ExtractIPAddresses.mjs index 1cca2098..8d36a783 100644 --- a/src/core/operations/ExtractIPAddresses.mjs +++ b/src/core/operations/ExtractIPAddresses.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { search } from "../lib/Extract"; +import Operation from "../Operation.mjs"; +import { search } from "../lib/Extract.mjs"; /** * Extract IP addresses operation diff --git a/src/core/operations/ExtractMACAddresses.mjs b/src/core/operations/ExtractMACAddresses.mjs index 9c3c2a5b..d75b1a69 100644 --- a/src/core/operations/ExtractMACAddresses.mjs +++ b/src/core/operations/ExtractMACAddresses.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { search } from "../lib/Extract"; +import Operation from "../Operation.mjs"; +import { search } from "../lib/Extract.mjs"; /** * Extract MAC addresses operation diff --git a/src/core/operations/ExtractURLs.mjs b/src/core/operations/ExtractURLs.mjs index 5e76c6d9..a5b26515 100644 --- a/src/core/operations/ExtractURLs.mjs +++ b/src/core/operations/ExtractURLs.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { search, URL_REGEX } from "../lib/Extract"; +import Operation from "../Operation.mjs"; +import { search, URL_REGEX } from "../lib/Extract.mjs"; /** * Extract URLs operation diff --git a/src/core/operations/Filter.mjs b/src/core/operations/Filter.mjs index 64a97279..904bb65f 100644 --- a/src/core/operations/Filter.mjs +++ b/src/core/operations/Filter.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {INPUT_DELIM_OPTIONS} from "../lib/Delim"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {INPUT_DELIM_OPTIONS} from "../lib/Delim.mjs"; +import OperationError from "../errors/OperationError.mjs"; import XRegExp from "xregexp"; /** diff --git a/src/core/operations/FindReplace.mjs b/src/core/operations/FindReplace.mjs index af75fcb0..4fc1e43c 100644 --- a/src/core/operations/FindReplace.mjs +++ b/src/core/operations/FindReplace.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; import XRegExp from "xregexp"; /** diff --git a/src/core/operations/Fletcher16Checksum.mjs b/src/core/operations/Fletcher16Checksum.mjs index d2331823..b91ec2a8 100644 --- a/src/core/operations/Fletcher16Checksum.mjs +++ b/src/core/operations/Fletcher16Checksum.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * Fletcher-16 Checksum operation @@ -22,19 +22,20 @@ class Fletcher16Checksum extends Operation { 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"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { let a = 0, b = 0; + input = new Uint8Array(input); for (let i = 0; i < input.length; i++) { a = (a + input[i]) % 0xff; diff --git a/src/core/operations/Fletcher32Checksum.mjs b/src/core/operations/Fletcher32Checksum.mjs index 30f4bc69..29c74535 100644 --- a/src/core/operations/Fletcher32Checksum.mjs +++ b/src/core/operations/Fletcher32Checksum.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * Fletcher-32 Checksum operation @@ -22,19 +22,20 @@ class Fletcher32Checksum extends Operation { 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"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { let a = 0, b = 0; + input = new Uint8Array(input); for (let i = 0; i < input.length; i++) { a = (a + input[i]) % 0xffff; diff --git a/src/core/operations/Fletcher64Checksum.mjs b/src/core/operations/Fletcher64Checksum.mjs index 56afe42e..1d0d5bd9 100644 --- a/src/core/operations/Fletcher64Checksum.mjs +++ b/src/core/operations/Fletcher64Checksum.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * Fletcher-64 Checksum operation @@ -22,19 +22,20 @@ class Fletcher64Checksum extends Operation { 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"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { let a = 0, b = 0; + input = new Uint8Array(input); for (let i = 0; i < input.length; i++) { a = (a + input[i]) % 0xffffffff; diff --git a/src/core/operations/Fletcher8Checksum.mjs b/src/core/operations/Fletcher8Checksum.mjs index a6dc0e4d..1200c00c 100644 --- a/src/core/operations/Fletcher8Checksum.mjs +++ b/src/core/operations/Fletcher8Checksum.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * Fletcher-8 Checksum operation @@ -22,19 +22,20 @@ class Fletcher8Checksum extends Operation { 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"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { let a = 0, b = 0; + input = new Uint8Array(input); for (let i = 0; i < input.length; i++) { a = (a + input[i]) % 0xf; diff --git a/src/core/operations/FlipImage.mjs b/src/core/operations/FlipImage.mjs index 593809e9..b04469ca 100644 --- a/src/core/operations/FlipImage.mjs +++ b/src/core/operations/FlipImage.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; -import { toBase64 } from "../lib/Base64"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; import jimp from "jimp"; /** @@ -25,8 +26,8 @@ class FlipImage extends Operation { this.module = "Image"; this.description = "Flips an image along its X or Y axis."; this.infoURL = ""; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -38,24 +39,24 @@ class FlipImage extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ async run(input, args) { const [flipAxis] = args; - if (!isImage(input)) { + if (!isImage(new Uint8Array(input))) { throw new OperationError("Invalid input file type."); } let image; try { - image = await jimp.read(Buffer.from(input)); + image = await jimp.read(input); } catch (err) { throw new OperationError(`Error loading image. (${err})`); } try { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Flipping image..."); switch (flipAxis){ case "Horizontal": @@ -66,8 +67,13 @@ class FlipImage extends Operation { break; } - const imageBuffer = await image.getBufferAsync(jimp.AUTO); - return [...imageBuffer]; + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; } catch (err) { throw new OperationError(`Error flipping image. (${err})`); } @@ -75,18 +81,19 @@ class FlipImage extends Operation { /** * Displays the flipped image using HTML for web apps - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/Fork.mjs b/src/core/operations/Fork.mjs index 02aba3e8..521ca271 100644 --- a/src/core/operations/Fork.mjs +++ b/src/core/operations/Fork.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Recipe from "../Recipe"; -import Dish from "../Dish"; +import Operation from "../Operation.mjs"; +import Recipe from "../Recipe.mjs"; +import Dish from "../Dish.mjs"; /** * Fork operation diff --git a/src/core/operations/FormatMACAddresses.mjs b/src/core/operations/FormatMACAddresses.mjs index 1002877d..41fac594 100644 --- a/src/core/operations/FormatMACAddresses.mjs +++ b/src/core/operations/FormatMACAddresses.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Format MAC addresses operation diff --git a/src/core/operations/FrequencyDistribution.mjs b/src/core/operations/FrequencyDistribution.mjs index 13eb53e1..f0e1f592 100644 --- a/src/core/operations/FrequencyDistribution.mjs +++ b/src/core/operations/FrequencyDistribution.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Frequency distribution operation diff --git a/src/core/operations/FromBCD.mjs b/src/core/operations/FromBCD.mjs index a87c1101..acbe468b 100644 --- a/src/core/operations/FromBCD.mjs +++ b/src/core/operations/FromBCD.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; -import {ENCODING_SCHEME, ENCODING_LOOKUP, FORMAT} from "../lib/BCD"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {ENCODING_SCHEME, ENCODING_LOOKUP, FORMAT} from "../lib/BCD.mjs"; import BigNumber from "bignumber.js"; /** diff --git a/src/core/operations/FromBase.mjs b/src/core/operations/FromBase.mjs index a44eaf60..f374bdd0 100644 --- a/src/core/operations/FromBase.mjs +++ b/src/core/operations/FromBase.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import BigNumber from "bignumber.js"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * From Base operation diff --git a/src/core/operations/FromBase32.mjs b/src/core/operations/FromBase32.mjs index 3518ec5f..a204b830 100644 --- a/src/core/operations/FromBase32.mjs +++ b/src/core/operations/FromBase32.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * From Base32 operation diff --git a/src/core/operations/FromBase58.mjs b/src/core/operations/FromBase58.mjs index 8b0db19f..64668c3f 100644 --- a/src/core/operations/FromBase58.mjs +++ b/src/core/operations/FromBase58.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; -import {ALPHABET_OPTIONS} from "../lib/Base58"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {ALPHABET_OPTIONS} from "../lib/Base58.mjs"; /** * From Base58 operation diff --git a/src/core/operations/FromBase62.mjs b/src/core/operations/FromBase62.mjs index 525f2e2f..c2f54ec0 100644 --- a/src/core/operations/FromBase62.mjs +++ b/src/core/operations/FromBase62.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import BigNumber from "bignumber.js"; -import Utils from "../Utils"; +import Utils from "../Utils.mjs"; /** diff --git a/src/core/operations/FromBase64.mjs b/src/core/operations/FromBase64.mjs index be049802..6ee01b65 100644 --- a/src/core/operations/FromBase64.mjs +++ b/src/core/operations/FromBase64.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {fromBase64, ALPHABET_OPTIONS} from "../lib/Base64"; +import Operation from "../Operation.mjs"; +import {fromBase64, ALPHABET_OPTIONS} from "../lib/Base64.mjs"; /** * From Base64 operation @@ -106,9 +106,9 @@ class FromBase64 extends Operation { } /** - * @param {ArrayBuffer} input + * @param {string} input * @param {Object[]} args - * @returns {string} + * @returns {byteArray} */ run(input, args) { const [alphabet, removeNonAlphChars] = args; diff --git a/src/core/operations/FromBase85.mjs b/src/core/operations/FromBase85.mjs index 2fec8e2e..c874d5dc 100644 --- a/src/core/operations/FromBase85.mjs +++ b/src/core/operations/FromBase85.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import Utils from "../Utils"; -import {alphabetName, ALPHABET_OPTIONS} from "../lib/Base85"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; +import {alphabetName, ALPHABET_OPTIONS} from "../lib/Base85.mjs"; /** * From Base85 operation diff --git a/src/core/operations/FromBinary.mjs b/src/core/operations/FromBinary.mjs index a7ced7e3..e7ca5045 100644 --- a/src/core/operations/FromBinary.mjs +++ b/src/core/operations/FromBinary.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {BIN_DELIM_OPTIONS} from "../lib/Delim"; -import {fromBinary} from "../lib/Binary"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {BIN_DELIM_OPTIONS} from "../lib/Delim.mjs"; +import {fromBinary} from "../lib/Binary.mjs"; /** * From Binary operation diff --git a/src/core/operations/FromBraille.mjs b/src/core/operations/FromBraille.mjs index 61fa1452..adbcff91 100644 --- a/src/core/operations/FromBraille.mjs +++ b/src/core/operations/FromBraille.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {BRAILLE_LOOKUP} from "../lib/Braille"; +import Operation from "../Operation.mjs"; +import {BRAILLE_LOOKUP} from "../lib/Braille.mjs"; /** * From Braille operation diff --git a/src/core/operations/FromCaseInsensitiveRegex.mjs b/src/core/operations/FromCaseInsensitiveRegex.mjs index 36cd9b14..31cbfb99 100644 --- a/src/core/operations/FromCaseInsensitiveRegex.mjs +++ b/src/core/operations/FromCaseInsensitiveRegex.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * From Case Insensitive Regex operation diff --git a/src/core/operations/FromCharcode.mjs b/src/core/operations/FromCharcode.mjs index 3ae54b6c..052b1162 100644 --- a/src/core/operations/FromCharcode.mjs +++ b/src/core/operations/FromCharcode.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {DELIM_OPTIONS} from "../lib/Delim"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import { DELIM_OPTIONS } from "../lib/Delim.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * From Charcode operation @@ -61,7 +62,7 @@ class FromCharcode extends Operation { return []; } - if (base !== 16 && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false); + if (base !== 16 && isWorkerEnvironment()) self.setOption("attemptHighlight", false); // Split into groups of 2 if the whole string is concatenated and // too long to be a single character diff --git a/src/core/operations/FromDecimal.mjs b/src/core/operations/FromDecimal.mjs index 1d0f272c..4248ce94 100644 --- a/src/core/operations/FromDecimal.mjs +++ b/src/core/operations/FromDecimal.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {DELIM_OPTIONS} from "../lib/Delim"; -import {fromDecimal} from "../lib/Decimal"; +import Operation from "../Operation.mjs"; +import {DELIM_OPTIONS} from "../lib/Delim.mjs"; +import {fromDecimal} from "../lib/Decimal.mjs"; /** * From Decimal operation diff --git a/src/core/operations/FromHTMLEntity.mjs b/src/core/operations/FromHTMLEntity.mjs index 0a07962b..3d53a0e7 100644 --- a/src/core/operations/FromHTMLEntity.mjs +++ b/src/core/operations/FromHTMLEntity.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * From HTML Entity operation @@ -19,7 +19,7 @@ class FromHTMLEntity extends Operation { super(); this.name = "From HTML Entity"; - this.module = "Default"; + this.module = "Encodings"; this.description = "Converts HTML entities back to characters

e.g. &amp; becomes &"; this.infoURL = "https://wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references"; this.inputType = "string"; @@ -40,7 +40,7 @@ class FromHTMLEntity extends Operation { * @returns {string} */ run(input, args) { - const regex = /&(#?x?[a-zA-Z0-9]{1,8});/g; + const regex = /&(#?x?[a-zA-Z0-9]{1,20});/g; let output = "", m, i = 0; @@ -84,11 +84,38 @@ class FromHTMLEntity extends Operation { * Lookup table to translate HTML entity codes to their byte values. */ const entityToByte = { + "Tab": 9, + "NewLine": 10, + "excl": 33, "quot": 34, + "num": 35, + "dollar": 36, + "percnt": 37, "amp": 38, "apos": 39, + "lpar": 40, + "rpar": 41, + "ast": 42, + "plus": 43, + "comma": 44, + "period": 46, + "sol": 47, + "colon": 58, + "semi": 59, "lt": 60, + "equals": 61, "gt": 62, + "quest": 63, + "commat": 64, + "lsqb": 91, + "bsol": 92, + "rsqb": 93, + "Hat": 94, + "lowbar": 95, + "grave": 96, + "lcub": 123, + "verbar": 124, + "rcub": 125, "nbsp": 160, "iexcl": 161, "cent": 162, @@ -185,14 +212,140 @@ const entityToByte = { "yacute": 253, "thorn": 254, "yuml": 255, + "Amacr": 256, + "amacr": 257, + "Abreve": 258, + "abreve": 259, + "Aogon": 260, + "aogon": 261, + "Cacute": 262, + "cacute": 263, + "Ccirc": 264, + "ccirc": 265, + "Cdot": 266, + "cdot": 267, + "Ccaron": 268, + "ccaron": 269, + "Dcaron": 270, + "dcaron": 271, + "Dstrok": 272, + "dstrok": 273, + "Emacr": 274, + "emacr": 275, + "Edot": 278, + "edot": 279, + "Eogon": 280, + "eogon": 281, + "Ecaron": 282, + "ecaron": 283, + "Gcirc": 284, + "gcirc": 285, + "Gbreve": 286, + "gbreve": 287, + "Gdot": 288, + "gdot": 289, + "Gcedil": 290, + "Hcirc": 292, + "hcirc": 293, + "Hstrok": 294, + "hstrok": 295, + "Itilde": 296, + "itilde": 297, + "Imacr": 298, + "imacr": 299, + "Iogon": 302, + "iogon": 303, + "Idot": 304, + "imath": 305, + "IJlig": 306, + "ijlig": 307, + "Jcirc": 308, + "jcirc": 309, + "Kcedil": 310, + "kcedil": 311, + "kgreen": 312, + "Lacute": 313, + "lacute": 314, + "Lcedil": 315, + "lcedil": 316, + "Lcaron": 317, + "lcaron": 318, + "Lmidot": 319, + "lmidot": 320, + "Lstrok": 321, + "lstrok": 322, + "Nacute": 323, + "nacute": 324, + "Ncedil": 325, + "ncedil": 326, + "Ncaron": 327, + "ncaron": 328, + "napos": 329, + "ENG": 330, + "eng": 331, + "Omacr": 332, + "omacr": 333, + "Odblac": 336, + "odblac": 337, "OElig": 338, "oelig": 339, + "Racute": 340, + "racute": 341, + "Rcedil": 342, + "rcedil": 343, + "Rcaron": 344, + "rcaron": 345, + "Sacute": 346, + "sacute": 347, + "Scirc": 348, + "scirc": 349, + "Scedil": 350, + "scedil": 351, "Scaron": 352, "scaron": 353, + "Tcedil": 354, + "tcedil": 355, + "Tcaron": 356, + "tcaron": 357, + "Tstrok": 358, + "tstrok": 359, + "Utilde": 360, + "utilde": 361, + "Umacr": 362, + "umacr": 363, + "Ubreve": 364, + "ubreve": 365, + "Uring": 366, + "uring": 367, + "Udblac": 368, + "udblac": 369, + "Uogon": 370, + "uogon": 371, + "Wcirc": 372, + "wcirc": 373, + "Ycirc": 374, + "ycirc": 375, "Yuml": 376, + "Zacute": 377, + "zacute": 378, + "Zdot": 379, + "zdot": 380, + "Zcaron": 381, + "zcaron": 382, "fnof": 402, + "imped": 437, + "gacute": 501, + "jmath": 567, "circ": 710, + "caron": 711, + "breve": 728, + "dot": 729, + "ring": 730, + "ogon": 731, "tilde": 732, + "dblac": 733, + "DownBreve": 785, + "UnderBar": 818, "Alpha": 913, "Beta": 914, "Gamma": 915, @@ -244,16 +397,124 @@ const entityToByte = { "omega": 969, "thetasym": 977, "upsih": 978, + "straightphi": 981, "piv": 982, + "Gammad": 988, + "gammad": 989, + "kappav": 1008, + "rhov": 1009, + "epsi": 1013, + "bepsi": 1014, + "IOcy": 1025, + "DJcy": 1026, + "GJcy": 1027, + "Jukcy": 1028, + "DScy": 1029, + "Iukcy": 1030, + "YIcy": 1031, + "Jsercy": 1032, + "LJcy": 1033, + "NJcy": 1034, + "TSHcy": 1035, + "KJcy": 1036, + "Ubrcy": 1038, + "DZcy": 1039, + "Acy": 1040, + "Bcy": 1041, + "Vcy": 1042, + "Gcy": 1043, + "Dcy": 1044, + "IEcy": 1045, + "ZHcy": 1046, + "Zcy": 1047, + "Icy": 1048, + "Jcy": 1049, + "Kcy": 1050, + "Lcy": 1051, + "Mcy": 1052, + "Ncy": 1053, + "Ocy": 1054, + "Pcy": 1055, + "Rcy": 1056, + "Scy": 1057, + "Tcy": 1058, + "Ucy": 1059, + "Fcy": 1060, + "KHcy": 1061, + "TScy": 1062, + "CHcy": 1063, + "SHcy": 1064, + "SHCHcy": 1065, + "HARDcy": 1066, + "Ycy": 1067, + "SOFTcy": 1068, + "Ecy": 1069, + "YUcy": 1070, + "YAcy": 1071, + "acy": 1072, + "bcy": 1073, + "vcy": 1074, + "gcy": 1075, + "dcy": 1076, + "iecy": 1077, + "zhcy": 1078, + "zcy": 1079, + "icy": 1080, + "jcy": 1081, + "kcy": 1082, + "lcy": 1083, + "mcy": 1084, + "ncy": 1085, + "ocy": 1086, + "pcy": 1087, + "rcy": 1088, + "scy": 1089, + "tcy": 1090, + "ucy": 1091, + "fcy": 1092, + "khcy": 1093, + "tscy": 1094, + "chcy": 1095, + "shcy": 1096, + "shchcy": 1097, + "hardcy": 1098, + "ycy": 1099, + "softcy": 1100, + "ecy": 1101, + "yucy": 1102, + "yacy": 1103, + "iocy": 1105, + "djcy": 1106, + "gjcy": 1107, + "jukcy": 1108, + "dscy": 1109, + "iukcy": 1110, + "yicy": 1111, + "jsercy": 1112, + "ljcy": 1113, + "njcy": 1114, + "tshcy": 1115, + "kjcy": 1116, + "ubrcy": 1118, + "dzcy": 1119, "ensp": 8194, "emsp": 8195, + "emsp13": 8196, + "emsp14": 8197, + "numsp": 8199, + "puncsp": 8200, "thinsp": 8201, + "hairsp": 8202, + "ZeroWidthSpace": 8203, "zwnj": 8204, "zwj": 8205, "lrm": 8206, "rlm": 8207, + "hyphen": 8208, "ndash": 8211, "mdash": 8212, + "horbar": 8213, + "Verbar": 8214, "lsquo": 8216, "rsquo": 8217, "sbquo": 8218, @@ -263,81 +524,1016 @@ const entityToByte = { "dagger": 8224, "Dagger": 8225, "bull": 8226, + "nldr": 8229, "hellip": 8230, "permil": 8240, + "pertenk": 8241, "prime": 8242, "Prime": 8243, + "tprime": 8244, + "bprime": 8245, "lsaquo": 8249, "rsaquo": 8250, "oline": 8254, + "caret": 8257, + "hybull": 8259, "frasl": 8260, + "bsemi": 8271, + "qprime": 8279, + "MediumSpace": 8287, + "NoBreak": 8288, + "ApplyFunction": 8289, + "InvisibleTimes": 8290, + "InvisibleComma": 8291, "euro": 8364, + "tdot": 8411, + "TripleDot": 8411, + "DotDot": 8412, + "Copf": 8450, + "incare": 8453, + "gscr": 8458, + "hamilt": 8459, + "Hfr": 8460, + "quaternions": 8461, + "planckh": 8462, + "planck": 8463, + "Iscr": 8464, "image": 8465, + "Lscr": 8466, + "ell": 8467, + "Nopf": 8469, + "numero": 8470, + "copysr": 8471, "weierp": 8472, + "Popf": 8473, + "rationals": 8474, + "Rscr": 8475, "real": 8476, + "reals": 8477, + "rx": 8478, "trade": 8482, + "integers": 8484, + "ohm": 8486, + "mho": 8487, + "Zfr": 8488, + "iiota": 8489, + "angst": 8491, + "bernou": 8492, + "Cfr": 8493, + "escr": 8495, + "Escr": 8496, + "Fscr": 8497, + "phmmat": 8499, + "order": 8500, "alefsym": 8501, + "beth": 8502, + "gimel": 8503, + "daleth": 8504, + "CapitalDifferentialD": 8517, + "DifferentialD": 8518, + "ExponentialE": 8519, + "ImaginaryI": 8520, + "frac13": 8531, + "frac23": 8532, + "frac15": 8533, + "frac25": 8534, + "frac35": 8535, + "frac45": 8536, + "frac16": 8537, + "frac56": 8538, + "frac18": 8539, + "frac38": 8540, + "frac58": 8541, + "frac78": 8542, "larr": 8592, "uarr": 8593, "rarr": 8594, "darr": 8595, "harr": 8596, + "varr": 8597, + "nwarr": 8598, + "nearr": 8599, + "searr": 8600, + "swarr": 8601, + "nlarr": 8602, + "nrarr": 8603, + "rarrw": 8605, + "Larr": 8606, + "Uarr": 8607, + "Rarr": 8608, + "Darr": 8609, + "larrtl": 8610, + "rarrtl": 8611, + "LeftTeeArrow": 8612, + "UpTeeArrow": 8613, + "map": 8614, + "DownTeeArrow": 8615, + "larrhk": 8617, + "rarrhk": 8618, + "larrlp": 8619, + "rarrlp": 8620, + "harrw": 8621, + "nharr": 8622, + "lsh": 8624, + "rsh": 8625, + "ldsh": 8626, + "rdsh": 8627, "crarr": 8629, + "cularr": 8630, + "curarr": 8631, + "olarr": 8634, + "orarr": 8635, + "lharu": 8636, + "lhard": 8637, + "uharr": 8638, + "uharl": 8639, + "rharu": 8640, + "rhard": 8641, + "dharr": 8642, + "dharl": 8643, + "rlarr": 8644, + "udarr": 8645, + "lrarr": 8646, + "llarr": 8647, + "uuarr": 8648, + "rrarr": 8649, + "ddarr": 8650, + "lrhar": 8651, + "rlhar": 8652, + "nlArr": 8653, + "nhArr": 8654, + "nrArr": 8655, "lArr": 8656, "uArr": 8657, "rArr": 8658, "dArr": 8659, "hArr": 8660, + "vArr": 8661, + "nwArr": 8662, + "neArr": 8663, + "seArr": 8664, + "swArr": 8665, + "lAarr": 8666, + "rAarr": 8667, + "zigrarr": 8669, + "larrb": 8676, + "rarrb": 8677, + "duarr": 8693, + "loarr": 8701, + "roarr": 8702, + "hoarr": 8703, "forall": 8704, + "comp": 8705, "part": 8706, "exist": 8707, + "nexist": 8708, "empty": 8709, "nabla": 8711, "isin": 8712, "notin": 8713, "ni": 8715, + "notni": 8716, "prod": 8719, + "coprod": 8720, "sum": 8721, "minus": 8722, + "mnplus": 8723, + "plusdo": 8724, + "setmn": 8726, "lowast": 8727, + "compfn": 8728, "radic": 8730, "prop": 8733, "infin": 8734, + "angrt": 8735, "ang": 8736, + "angmsd": 8737, + "angsph": 8738, + "mid": 8739, + "nmid": 8740, + "par": 8741, + "npar": 8742, "and": 8743, "or": 8744, "cap": 8745, "cup": 8746, "int": 8747, + "Int": 8748, + "tint": 8749, + "conint": 8750, + "Conint": 8751, + "Cconint": 8752, + "cwint": 8753, + "cwconint": 8754, + "awconint": 8755, "there4": 8756, + "becaus": 8757, + "ratio": 8758, + "Colon": 8759, + "minusd": 8760, + "mDDot": 8762, + "homtht": 8763, "sim": 8764, + "bsim": 8765, + "ac": 8766, + "acd": 8767, + "wreath": 8768, + "nsim": 8769, + "esim": 8770, + "sime": 8771, + "nsime": 8772, "cong": 8773, + "simne": 8774, + "ncong": 8775, "asymp": 8776, + "nap": 8777, + "ape": 8778, + "apid": 8779, + "bcong": 8780, + "asympeq": 8781, + "bump": 8782, + "bumpe": 8783, + "esdot": 8784, + "eDot": 8785, + "efDot": 8786, + "erDot": 8787, + "colone": 8788, + "ecolon": 8789, + "ecir": 8790, + "cire": 8791, + "wedgeq": 8793, + "veeeq": 8794, + "trie": 8796, + "equest": 8799, "ne": 8800, "equiv": 8801, + "nequiv": 8802, "le": 8804, "ge": 8805, + "lE": 8806, + "gE": 8807, + "lnE": 8808, + "gnE": 8809, + "Lt": 8810, + "Gt": 8811, + "twixt": 8812, + "NotCupCap": 8813, + "nlt": 8814, + "ngt": 8815, + "nle": 8816, + "nge": 8817, + "lsim": 8818, + "gsim": 8819, + "nlsim": 8820, + "ngsim": 8821, + "lg": 8822, + "gl": 8823, + "ntlg": 8824, + "ntgl": 8825, + "pr": 8826, + "sc": 8827, + "prcue": 8828, + "sccue": 8829, + "prsim": 8830, + "scsim": 8831, + "npr": 8832, + "nsc": 8833, "sub": 8834, "sup": 8835, "nsub": 8836, + "nsup": 8837, "sube": 8838, "supe": 8839, + "nsube": 8840, + "nsupe": 8841, + "subne": 8842, + "supne": 8843, + "cupdot": 8845, + "uplus": 8846, + "sqsub": 8847, + "sqsup": 8848, + "sqsube": 8849, + "sqsupe": 8850, + "sqcap": 8851, + "sqcup": 8852, "oplus": 8853, + "ominus": 8854, "otimes": 8855, + "osol": 8856, + "odot": 8857, + "ocir": 8858, + "oast": 8859, + "odash": 8861, + "plusb": 8862, + "minusb": 8863, + "timesb": 8864, + "sdotb": 8865, + "vdash": 8866, + "dashv": 8867, + "top": 8868, "perp": 8869, + "models": 8871, + "vDash": 8872, + "Vdash": 8873, + "Vvdash": 8874, + "VDash": 8875, + "nvdash": 8876, + "nvDash": 8877, + "nVdash": 8878, + "nVDash": 8879, + "prurel": 8880, + "vltri": 8882, + "vrtri": 8883, + "ltrie": 8884, + "rtrie": 8885, + "origof": 8886, + "imof": 8887, + "mumap": 8888, + "hercon": 8889, + "intcal": 8890, + "veebar": 8891, + "barvee": 8893, + "angrtvb": 8894, + "lrtri": 8895, + "xwedge": 8896, + "xvee": 8897, + "xcap": 8898, + "xcup": 8899, + "diam": 8900, "sdot": 8901, + "sstarf": 8902, + "divonx": 8903, + "bowtie": 8904, + "ltimes": 8905, + "rtimes": 8906, + "lthree": 8907, + "rthree": 8908, + "bsime": 8909, + "cuvee": 8910, + "cuwed": 8911, + "Sub": 8912, + "Sup": 8913, + "Cap": 8914, + "Cup": 8915, + "fork": 8916, + "epar": 8917, + "ltdot": 8918, + "gtdot": 8919, + "Ll": 8920, + "Gg": 8921, + "leg": 8922, + "gel": 8923, + "cuepr": 8926, + "cuesc": 8927, + "nprcue": 8928, + "nsccue": 8929, + "nsqsube": 8930, + "nsqsupe": 8931, + "lnsim": 8934, + "gnsim": 8935, + "prnsim": 8936, + "scnsim": 8937, + "nltri": 8938, + "nrtri": 8939, + "nltrie": 8940, + "nrtrie": 8941, "vellip": 8942, + "ctdot": 8943, + "utdot": 8944, + "dtdot": 8945, + "disin": 8946, + "isinsv": 8947, + "isins": 8948, + "isindot": 8949, + "notinvc": 8950, + "notinvb": 8951, + "isinE": 8953, + "nisd": 8954, + "xnis": 8955, + "nis": 8956, + "notnivc": 8957, + "notnivb": 8958, + "barwed": 8965, + "Barwed": 8966, "lceil": 8968, "rceil": 8969, "lfloor": 8970, "rfloor": 8971, + "drcrop": 8972, + "dlcrop": 8973, + "urcrop": 8974, + "ulcrop": 8975, + "bnot": 8976, + "profline": 8978, + "profsurf": 8979, + "telrec": 8981, + "target": 8982, + "ulcorn": 8988, + "urcorn": 8989, + "dlcorn": 8990, + "drcorn": 8991, + "frown": 8994, + "smile": 8995, "lang": 9001, "rang": 9002, + "cylcty": 9005, + "profalar": 9006, + "topbot": 9014, + "ovbar": 9021, + "solbar": 9023, + "angzarr": 9084, + "lmoust": 9136, + "rmoust": 9137, + "tbrk": 9140, + "bbrk": 9141, + "bbrktbrk": 9142, + "OverParenthesis": 9180, + "UnderParenthesis": 9181, + "OverBrace": 9182, + "UnderBrace": 9183, + "trpezium": 9186, + "elinters": 9191, + "blank": 9251, + "oS": 9416, + "boxh": 9472, + "boxv": 9474, + "boxdr": 9484, + "boxdl": 9488, + "boxur": 9492, + "boxul": 9496, + "boxvr": 9500, + "boxvl": 9508, + "boxhd": 9516, + "boxhu": 9524, + "boxvh": 9532, + "boxH": 9552, + "boxV": 9553, + "boxdR": 9554, + "boxDr": 9555, + "boxDR": 9556, + "boxdL": 9557, + "boxDl": 9558, + "boxDL": 9559, + "boxuR": 9560, + "boxUr": 9561, + "boxUR": 9562, + "boxuL": 9563, + "boxUl": 9564, + "boxUL": 9565, + "boxvR": 9566, + "boxVr": 9567, + "boxVR": 9568, + "boxvL": 9569, + "boxVl": 9570, + "boxVL": 9571, + "boxHd": 9572, + "boxhD": 9573, + "boxHD": 9574, + "boxHu": 9575, + "boxhU": 9576, + "boxHU": 9577, + "boxvH": 9578, + "boxVh": 9579, + "boxVH": 9580, + "uhblk": 9600, + "lhblk": 9604, + "block": 9608, + "blk14": 9617, + "blk12": 9618, + "blk34": 9619, + "squ": 9633, + "squf": 9642, + "EmptyVerySmallSquare": 9643, + "rect": 9645, + "marker": 9646, + "fltns": 9649, + "xutri": 9651, + "utrif": 9652, + "utri": 9653, + "rtrif": 9656, + "rtri": 9657, + "xdtri": 9661, + "dtrif": 9662, + "dtri": 9663, + "ltrif": 9666, + "ltri": 9667, "loz": 9674, + "cir": 9675, + "tridot": 9708, + "xcirc": 9711, + "ultri": 9720, + "urtri": 9721, + "lltri": 9722, + "EmptySmallSquare": 9723, + "FilledSmallSquare": 9724, + "starf": 9733, + "bigstar": 9733, + "star": 9734, + "phone": 9742, + "female": 9792, + "male": 9794, "spades": 9824, "clubs": 9827, "hearts": 9829, "diams": 9830, + "sung": 9834, + "flat": 9837, + "natur": 9838, + "sharp": 9839, + "check": 10003, + "cross": 10007, + "malt": 10016, + "sext": 10038, + "VerticalSeparator": 10072, + "lbbrk": 10098, + "rbbrk": 10099, + "lobrk": 10214, + "robrk": 10215, + "Lang": 10218, + "Rang": 10219, + "loang": 10220, + "roang": 10221, + "xlarr": 10229, + "xrarr": 10230, + "xharr": 10231, + "xlArr": 10232, + "xrArr": 10233, + "xhArr": 10234, + "xmap": 10236, + "dzigrarr": 10239, + "nvlArr": 10498, + "nvrArr": 10499, + "nvHarr": 10500, + "Map": 10501, + "lbarr": 10508, + "rbarr": 10509, + "lBarr": 10510, + "rBarr": 10511, + "RBarr": 10512, + "DDotrahd": 10513, + "UpArrowBar": 10514, + "DownArrowBar": 10515, + "Rarrtl": 10518, + "latail": 10521, + "ratail": 10522, + "lAtail": 10523, + "rAtail": 10524, + "larrfs": 10525, + "rarrfs": 10526, + "larrbfs": 10527, + "rarrbfs": 10528, + "nwarhk": 10531, + "nearhk": 10532, + "searhk": 10533, + "swarhk": 10534, + "nwnear": 10535, + "nesear": 10536, + "seswar": 10537, + "swnwar": 10538, + "rarrc": 10547, + "cudarrr": 10549, + "ldca": 10550, + "rdca": 10551, + "cudarrl": 10552, + "larrpl": 10553, + "curarrm": 10556, + "cularrp": 10557, + "rarrpl": 10565, + "harrcir": 10568, + "Uarrocir": 10569, + "lurdshar": 10570, + "ldrushar": 10571, + "LeftRightVector": 10574, + "RightUpDownVector": 10575, + "DownLeftRightVector": 10576, + "LeftUpDownVector": 10577, + "LeftVectorBar": 10578, + "RightVectorBar": 10579, + "RightUpVectorBar": 10580, + "RightDownVectorBar": 10581, + "DownLeftVectorBar": 10582, + "DownRightVectorBar": 10583, + "LeftUpVectorBar": 10584, + "LeftDownVectorBar": 10585, + "LeftTeeVector": 10586, + "RightTeeVector": 10587, + "RightUpTeeVector": 10588, + "RightDownTeeVector": 10589, + "DownLeftTeeVector": 10590, + "DownRightTeeVector": 10591, + "LeftUpTeeVector": 10592, + "LeftDownTeeVector": 10593, + "lHar": 10594, + "uHar": 10595, + "rHar": 10596, + "dHar": 10597, + "luruhar": 10598, + "ldrdhar": 10599, + "ruluhar": 10600, + "rdldhar": 10601, + "lharul": 10602, + "llhard": 10603, + "rharul": 10604, + "lrhard": 10605, + "udhar": 10606, + "duhar": 10607, + "RoundImplies": 10608, + "erarr": 10609, + "simrarr": 10610, + "larrsim": 10611, + "rarrsim": 10612, + "rarrap": 10613, + "ltlarr": 10614, + "gtrarr": 10616, + "subrarr": 10617, + "suplarr": 10619, + "lfisht": 10620, + "rfisht": 10621, + "ufisht": 10622, + "dfisht": 10623, + "lopar": 10629, + "ropar": 10630, + "lbrke": 10635, + "rbrke": 10636, + "lbrkslu": 10637, + "rbrksld": 10638, + "lbrksld": 10639, + "rbrkslu": 10640, + "langd": 10641, + "rangd": 10642, + "lparlt": 10643, + "rpargt": 10644, + "gtlPar": 10645, + "ltrPar": 10646, + "vzigzag": 10650, + "vangrt": 10652, + "angrtvbd": 10653, + "ange": 10660, + "range": 10661, + "dwangle": 10662, + "uwangle": 10663, + "angmsdaa": 10664, + "angmsdab": 10665, + "angmsdac": 10666, + "angmsdad": 10667, + "angmsdae": 10668, + "angmsdaf": 10669, + "angmsdag": 10670, + "angmsdah": 10671, + "bemptyv": 10672, + "demptyv": 10673, + "cemptyv": 10674, + "raemptyv": 10675, + "laemptyv": 10676, + "ohbar": 10677, + "omid": 10678, + "opar": 10679, + "operp": 10681, + "olcross": 10683, + "odsold": 10684, + "olcir": 10686, + "ofcir": 10687, + "olt": 10688, + "ogt": 10689, + "cirscir": 10690, + "cirE": 10691, + "solb": 10692, + "bsolb": 10693, + "boxbox": 10697, + "trisb": 10701, + "rtriltri": 10702, + "LeftTriangleBar": 10703, + "RightTriangleBar": 10704, + "race": 10714, + "iinfin": 10716, + "infintie": 10717, + "nvinfin": 10718, + "eparsl": 10723, + "smeparsl": 10724, + "eqvparsl": 10725, + "lozf": 10731, + "RuleDelayed": 10740, + "dsol": 10742, + "xodot": 10752, + "xoplus": 10753, + "xotime": 10754, + "xuplus": 10756, + "xsqcup": 10758, + "qint": 10764, + "fpartint": 10765, + "cirfnint": 10768, + "awint": 10769, + "rppolint": 10770, + "scpolint": 10771, + "npolint": 10772, + "pointint": 10773, + "quatint": 10774, + "intlarhk": 10775, + "pluscir": 10786, + "plusacir": 10787, + "simplus": 10788, + "plusdu": 10789, + "plussim": 10790, + "plustwo": 10791, + "mcomma": 10793, + "minusdu": 10794, + "loplus": 10797, + "roplus": 10798, + "Cross": 10799, + "timesd": 10800, + "timesbar": 10801, + "smashp": 10803, + "lotimes": 10804, + "rotimes": 10805, + "otimesas": 10806, + "Otimes": 10807, + "odiv": 10808, + "triplus": 10809, + "triminus": 10810, + "tritime": 10811, + "iprod": 10812, + "amalg": 10815, + "capdot": 10816, + "ncup": 10818, + "ncap": 10819, + "capand": 10820, + "cupor": 10821, + "cupcap": 10822, + "capcup": 10823, + "cupbrcap": 10824, + "capbrcup": 10825, + "cupcup": 10826, + "capcap": 10827, + "ccups": 10828, + "ccaps": 10829, + "ccupssm": 10832, + "And": 10835, + "Or": 10836, + "andand": 10837, + "oror": 10838, + "orslope": 10839, + "andslope": 10840, + "andv": 10842, + "orv": 10843, + "andd": 10844, + "ord": 10845, + "wedbar": 10847, + "sdote": 10854, + "simdot": 10858, + "congdot": 10861, + "easter": 10862, + "apacir": 10863, + "apE": 10864, + "eplus": 10865, + "pluse": 10866, + "Esim": 10867, + "Colone": 10868, + "Equal": 10869, + "eDDot": 10871, + "equivDD": 10872, + "ltcir": 10873, + "gtcir": 10874, + "ltquest": 10875, + "gtquest": 10876, + "les": 10877, + "ges": 10878, + "lesdot": 10879, + "gesdot": 10880, + "lesdoto": 10881, + "gesdoto": 10882, + "lesdotor": 10883, + "gesdotol": 10884, + "lap": 10885, + "gap": 10886, + "lne": 10887, + "gne": 10888, + "lnap": 10889, + "gnap": 10890, + "lEg": 10891, + "gEl": 10892, + "lsime": 10893, + "gsime": 10894, + "lsimg": 10895, + "gsiml": 10896, + "lgE": 10897, + "glE": 10898, + "lesges": 10899, + "gesles": 10900, + "els": 10901, + "egs": 10902, + "elsdot": 10903, + "egsdot": 10904, + "el": 10905, + "eg": 10906, + "siml": 10909, + "simg": 10910, + "simlE": 10911, + "simgE": 10912, + "LessLess": 10913, + "GreaterGreater": 10914, + "glj": 10916, + "gla": 10917, + "ltcc": 10918, + "gtcc": 10919, + "lescc": 10920, + "gescc": 10921, + "smt": 10922, + "lat": 10923, + "smte": 10924, + "late": 10925, + "bumpE": 10926, + "pre": 10927, + "sce": 10928, + "prE": 10931, + "scE": 10932, + "prnE": 10933, + "scnE": 10934, + "prap": 10935, + "scap": 10936, + "prnap": 10937, + "scnap": 10938, + "Pr": 10939, + "Sc": 10940, + "subdot": 10941, + "supdot": 10942, + "subplus": 10943, + "supplus": 10944, + "submult": 10945, + "supmult": 10946, + "subedot": 10947, + "supedot": 10948, + "subE": 10949, + "supE": 10950, + "subsim": 10951, + "supsim": 10952, + "subnE": 10955, + "supnE": 10956, + "csub": 10959, + "csup": 10960, + "csube": 10961, + "csupe": 10962, + "subsup": 10963, + "supsub": 10964, + "subsub": 10965, + "supsup": 10966, + "suphsub": 10967, + "supdsub": 10968, + "forkv": 10969, + "topfork": 10970, + "mlcp": 10971, + "Dashv": 10980, + "Vdashl": 10982, + "Barv": 10983, + "vBar": 10984, + "vBarv": 10985, + "Vbar": 10987, + "Not": 10988, + "bNot": 10989, + "rnmid": 10990, + "cirmid": 10991, + "midcir": 10992, + "topcir": 10993, + "nhpar": 10994, + "parsim": 10995, + "parsl": 11005, + "fflig": 64256, + "filig": 64257, + "fllig": 64258, + "ffilig": 64259, + "ffllig": 64260, + "Ascr": 119964, + "Cscr": 119966, + "Dscr": 119967, + "Gscr": 119970, + "Jscr": 119973, + "Kscr": 119974, + "Nscr": 119977, + "Oscr": 119978, + "Pscr": 119979, + "Qscr": 119980, + "Sscr": 119982, + "Tscr": 119983, + "Uscr": 119984, + "Vscr": 119985, + "Wscr": 119986, + "Xscr": 119987, + "Yscr": 119988, + "Zscr": 119989, + "ascr": 119990, + "bscr": 119991, + "cscr": 119992, + "dscr": 119993, + "fscr": 119995, + "hscr": 119997, + "iscr": 119998, + "jscr": 119999, + "kscr": 120000, + "lscr": 120001, + "mscr": 120002, + "nscr": 120003, + "pscr": 120005, + "qscr": 120006, + "rscr": 120007, + "sscr": 120008, + "tscr": 120009, + "uscr": 120010, + "vscr": 120011, + "wscr": 120012, + "xscr": 120013, + "yscr": 120014, + "zscr": 120015, + "Afr": 120068, + "Bfr": 120069, + "Dfr": 120071, + "Efr": 120072, + "Ffr": 120073, + "Gfr": 120074, + "Jfr": 120077, + "Kfr": 120078, + "Lfr": 120079, + "Mfr": 120080, + "Nfr": 120081, + "Ofr": 120082, + "Pfr": 120083, + "Qfr": 120084, + "Sfr": 120086, + "Tfr": 120087, + "Ufr": 120088, + "Vfr": 120089, + "Wfr": 120090, + "Xfr": 120091, + "Yfr": 120092, + "afr": 120094, + "bfr": 120095, + "cfr": 120096, + "dfr": 120097, + "efr": 120098, + "ffr": 120099, + "gfr": 120100, + "hfr": 120101, + "ifr": 120102, + "jfr": 120103, + "kfr": 120104, + "lfr": 120105, + "mfr": 120106, + "nfr": 120107, + "ofr": 120108, + "pfr": 120109, + "qfr": 120110, + "rfr": 120111, + "sfr": 120112, + "tfr": 120113, + "ufr": 120114, + "vfr": 120115, + "wfr": 120116, + "xfr": 120117, + "yfr": 120118, + "zfr": 120119, + "Aopf": 120120, + "Bopf": 120121, + "Dopf": 120123, + "Eopf": 120124, + "Fopf": 120125, + "Gopf": 120126, + "Iopf": 120128, + "Jopf": 120129, + "Kopf": 120130, + "Lopf": 120131, + "Mopf": 120132, + "Oopf": 120134, + "Sopf": 120138, + "Topf": 120139, + "Uopf": 120140, + "Vopf": 120141, + "Wopf": 120142, + "Xopf": 120143, + "Yopf": 120144, + "aopf": 120146, + "bopf": 120147, + "copf": 120148, + "dopf": 120149, + "eopf": 120150, + "fopf": 120151, + "gopf": 120152, + "hopf": 120153, + "iopf": 120154, + "jopf": 120155, + "kopf": 120156, + "lopf": 120157, + "mopf": 120158, + "nopf": 120159, + "oopf": 120160, + "popf": 120161, + "qopf": 120162, + "ropf": 120163, + "sopf": 120164, + "topf": 120165, + "uopf": 120166, + "vopf": 120167, + "wopf": 120168, + "xopf": 120169, + "yopf": 120170, + "zopf": 120171 }; export default FromHTMLEntity; diff --git a/src/core/operations/FromHex.mjs b/src/core/operations/FromHex.mjs index 658cf341..6f70de9a 100644 --- a/src/core/operations/FromHex.mjs +++ b/src/core/operations/FromHex.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {fromHex, FROM_HEX_DELIM_OPTIONS} from "../lib/Hex"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import {fromHex, FROM_HEX_DELIM_OPTIONS} from "../lib/Hex.mjs"; +import Utils from "../Utils.mjs"; /** * From Hex operation diff --git a/src/core/operations/FromHexContent.mjs b/src/core/operations/FromHexContent.mjs index 22e858e2..deb101bf 100644 --- a/src/core/operations/FromHexContent.mjs +++ b/src/core/operations/FromHexContent.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {fromHex} from "../lib/Hex"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {fromHex} from "../lib/Hex.mjs"; /** * From Hex Content operation diff --git a/src/core/operations/FromHexdump.mjs b/src/core/operations/FromHexdump.mjs index 7f4d9119..65889a4b 100644 --- a/src/core/operations/FromHexdump.mjs +++ b/src/core/operations/FromHexdump.mjs @@ -4,8 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {fromHex} from "../lib/Hex"; +import Operation from "../Operation.mjs"; +import { fromHex } from "../lib/Hex.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; + /** * From Hexdump operation @@ -55,7 +57,7 @@ class FromHexdump extends Operation { const w = (width - 13) / 4; // w should be the specified width of the hexdump and therefore a round number if (Math.floor(w) !== w || input.indexOf("\r") !== -1 || output.indexOf(13) !== -1) { - if (ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false); + if (isWorkerEnvironment()) self.setOption("attemptHighlight", false); } return output; } diff --git a/src/core/operations/FromMessagePack.mjs b/src/core/operations/FromMessagePack.mjs index cea7c498..a557ebbe 100644 --- a/src/core/operations/FromMessagePack.mjs +++ b/src/core/operations/FromMessagePack.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; import notepack from "notepack.io"; /** diff --git a/src/core/operations/FromMorseCode.mjs b/src/core/operations/FromMorseCode.mjs index 71613ad1..e7714912 100644 --- a/src/core/operations/FromMorseCode.mjs +++ b/src/core/operations/FromMorseCode.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {LETTER_DELIM_OPTIONS, WORD_DELIM_OPTIONS} from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {LETTER_DELIM_OPTIONS, WORD_DELIM_OPTIONS} from "../lib/Delim.mjs"; /** * From Morse Code operation diff --git a/src/core/operations/FromOctal.mjs b/src/core/operations/FromOctal.mjs index 41060807..6ff67965 100644 --- a/src/core/operations/FromOctal.mjs +++ b/src/core/operations/FromOctal.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {DELIM_OPTIONS} from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {DELIM_OPTIONS} from "../lib/Delim.mjs"; /** * From Octal operation diff --git a/src/core/operations/FromPunycode.mjs b/src/core/operations/FromPunycode.mjs index 96c0bcf9..2217ee3b 100644 --- a/src/core/operations/FromPunycode.mjs +++ b/src/core/operations/FromPunycode.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import punycode from "punycode"; /** diff --git a/src/core/operations/FromQuotedPrintable.mjs b/src/core/operations/FromQuotedPrintable.mjs index 61466e4e..bc123601 100644 --- a/src/core/operations/FromQuotedPrintable.mjs +++ b/src/core/operations/FromQuotedPrintable.mjs @@ -8,7 +8,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * From Quoted Printable operation diff --git a/src/core/operations/FromUNIXTimestamp.mjs b/src/core/operations/FromUNIXTimestamp.mjs index ee074ec0..ff390c58 100644 --- a/src/core/operations/FromUNIXTimestamp.mjs +++ b/src/core/operations/FromUNIXTimestamp.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import moment from "moment-timezone"; -import {UNITS} from "../lib/DateTime"; -import OperationError from "../errors/OperationError"; +import {UNITS} from "../lib/DateTime.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * From UNIX Timestamp operation diff --git a/src/core/operations/GOSTHash.mjs b/src/core/operations/GOSTHash.mjs new file mode 100644 index 00000000..d67a594c --- /dev/null +++ b/src/core/operations/GOSTHash.mjs @@ -0,0 +1,71 @@ +/** + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import GostDigest from "../vendor/gost/gostDigest.mjs"; +import {toHexFast} from "../lib/Hex.mjs"; + +/** + * GOST hash operation + */ +class GOSTHash extends Operation { + + /** + * GOSTHash constructor + */ + constructor() { + super(); + + this.name = "GOST hash"; + this.module = "Hashing"; + this.description = "The GOST hash function, defined in the standards GOST R 34.11-94 and GOST 34.311-95 is a 256-bit cryptographic hash function. It was initially defined in the Russian national standard GOST R 34.11-94 Information Technology – Cryptographic Information Security – Hash Function. The equivalent standard used by other member-states of the CIS is GOST 34.311-95.

This function must not be confused with a different Streebog hash function, which is defined in the new revision of the standard GOST R 34.11-2012.

The GOST hash function is based on the GOST block cipher."; + this.infoURL = "https://wikipedia.org/wiki/GOST_(hash_function)"; + this.inputType = "ArrayBuffer"; + this.outputType = "string"; + this.args = [ + { + "name": "S-Box", + "type": "option", + "value": [ + "D-A", + "D-SC", + "E-TEST", + "E-A", + "E-B", + "E-C", + "E-D", + "E-SC", + "E-Z", + "D-TEST" + ] + } + ]; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + try { + const sBox = args[1]; + const gostDigest = new GostDigest({ + name: "GOST R 34.11", + version: 1994, + sBox: sBox + }); + + return toHexFast(gostDigest.digest(input)); + } catch (err) { + throw new OperationError(err); + } + } + +} + +export default GOSTHash; diff --git a/src/core/operations/GenerateAllHashes.mjs b/src/core/operations/GenerateAllHashes.mjs index 6663d4e8..0b4560cc 100644 --- a/src/core/operations/GenerateAllHashes.mjs +++ b/src/core/operations/GenerateAllHashes.mjs @@ -4,32 +4,35 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import MD2 from "./MD2"; -import MD4 from "./MD4"; -import MD5 from "./MD5"; -import MD6 from "./MD6"; -import SHA0 from "./SHA0"; -import SHA1 from "./SHA1"; -import SHA2 from "./SHA2"; -import SHA3 from "./SHA3"; -import Keccak from "./Keccak"; -import Shake from "./Shake"; -import RIPEMD from "./RIPEMD"; -import HAS160 from "./HAS160"; -import Whirlpool from "./Whirlpool"; -import SSDEEP from "./SSDEEP"; -import CTPH from "./CTPH"; -import Fletcher8Checksum from "./Fletcher8Checksum"; -import Fletcher16Checksum from "./Fletcher16Checksum"; -import Fletcher32Checksum from "./Fletcher32Checksum"; -import Fletcher64Checksum from "./Fletcher64Checksum"; -import Adler32Checksum from "./Adler32Checksum"; -import CRC16Checksum from "./CRC16Checksum"; -import CRC32Checksum from "./CRC32Checksum"; -import BLAKE2b from "./BLAKE2b"; -import BLAKE2s from "./BLAKE2s"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import MD2 from "./MD2.mjs"; +import MD4 from "./MD4.mjs"; +import MD5 from "./MD5.mjs"; +import MD6 from "./MD6.mjs"; +import SHA0 from "./SHA0.mjs"; +import SHA1 from "./SHA1.mjs"; +import SHA2 from "./SHA2.mjs"; +import SHA3 from "./SHA3.mjs"; +import Keccak from "./Keccak.mjs"; +import Shake from "./Shake.mjs"; +import RIPEMD from "./RIPEMD.mjs"; +import HAS160 from "./HAS160.mjs"; +import Whirlpool from "./Whirlpool.mjs"; +import SSDEEP from "./SSDEEP.mjs"; +import CTPH from "./CTPH.mjs"; +import Fletcher8Checksum from "./Fletcher8Checksum.mjs"; +import Fletcher16Checksum from "./Fletcher16Checksum.mjs"; +import Fletcher32Checksum from "./Fletcher32Checksum.mjs"; +import Fletcher64Checksum from "./Fletcher64Checksum.mjs"; +import Adler32Checksum from "./Adler32Checksum.mjs"; +import CRC8Checksum from "./CRC8Checksum.mjs"; +import CRC16Checksum from "./CRC16Checksum.mjs"; +import CRC32Checksum from "./CRC32Checksum.mjs"; +import BLAKE2b from "./BLAKE2b.mjs"; +import BLAKE2s from "./BLAKE2s.mjs"; +import Streebog from "./Streebog.mjs"; +import GOSTHash from "./GOSTHash.mjs"; /** * Generate all hashes operation @@ -60,52 +63,56 @@ class GenerateAllHashes extends Operation { const arrayBuffer = input, str = Utils.arrayBufferToStr(arrayBuffer, false), byteArray = new Uint8Array(arrayBuffer), - output = "MD2: " + (new MD2()).run(arrayBuffer, []) + - "\nMD4: " + (new MD4()).run(arrayBuffer, []) + - "\nMD5: " + (new MD5()).run(arrayBuffer, []) + - "\nMD6: " + (new MD6()).run(str, []) + - "\nSHA0: " + (new SHA0()).run(arrayBuffer, []) + - "\nSHA1: " + (new SHA1()).run(arrayBuffer, []) + - "\nSHA2 224: " + (new SHA2()).run(arrayBuffer, ["224"]) + - "\nSHA2 256: " + (new SHA2()).run(arrayBuffer, ["256"]) + - "\nSHA2 384: " + (new SHA2()).run(arrayBuffer, ["384"]) + - "\nSHA2 512: " + (new SHA2()).run(arrayBuffer, ["512"]) + - "\nSHA3 224: " + (new SHA3()).run(arrayBuffer, ["224"]) + - "\nSHA3 256: " + (new SHA3()).run(arrayBuffer, ["256"]) + - "\nSHA3 384: " + (new SHA3()).run(arrayBuffer, ["384"]) + - "\nSHA3 512: " + (new SHA3()).run(arrayBuffer, ["512"]) + - "\nKeccak 224: " + (new Keccak()).run(arrayBuffer, ["224"]) + - "\nKeccak 256: " + (new Keccak()).run(arrayBuffer, ["256"]) + - "\nKeccak 384: " + (new Keccak()).run(arrayBuffer, ["384"]) + - "\nKeccak 512: " + (new Keccak()).run(arrayBuffer, ["512"]) + - "\nShake 128: " + (new Shake()).run(arrayBuffer, ["128", 256]) + - "\nShake 256: " + (new Shake()).run(arrayBuffer, ["256", 512]) + - "\nRIPEMD-128: " + (new RIPEMD()).run(arrayBuffer, ["128"]) + - "\nRIPEMD-160: " + (new RIPEMD()).run(arrayBuffer, ["160"]) + - "\nRIPEMD-256: " + (new RIPEMD()).run(arrayBuffer, ["256"]) + - "\nRIPEMD-320: " + (new RIPEMD()).run(arrayBuffer, ["320"]) + - "\nHAS-160: " + (new HAS160()).run(arrayBuffer, []) + - "\nWhirlpool-0: " + (new Whirlpool()).run(arrayBuffer, ["Whirlpool-0"]) + - "\nWhirlpool-T: " + (new Whirlpool()).run(arrayBuffer, ["Whirlpool-T"]) + - "\nWhirlpool: " + (new Whirlpool()).run(arrayBuffer, ["Whirlpool"]) + - "\nBLAKE2b-128: " + (new BLAKE2b).run(arrayBuffer, ["128", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2b-160: " + (new BLAKE2b).run(arrayBuffer, ["160", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2b-256: " + (new BLAKE2b).run(arrayBuffer, ["256", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2b-384: " + (new BLAKE2b).run(arrayBuffer, ["384", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2b-512: " + (new BLAKE2b).run(arrayBuffer, ["512", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2s-128: " + (new BLAKE2s).run(arrayBuffer, ["128", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2s-160: " + (new BLAKE2s).run(arrayBuffer, ["160", "Hex", {string: "", option: "UTF8"}]) + - "\nBLAKE2s-256: " + (new BLAKE2s).run(arrayBuffer, ["256", "Hex", {string: "", option: "UTF8"}]) + - "\nSSDEEP: " + (new SSDEEP()).run(str) + - "\nCTPH: " + (new CTPH()).run(str) + + output = "MD2: " + (new MD2()).run(arrayBuffer, []) + + "\nMD4: " + (new MD4()).run(arrayBuffer, []) + + "\nMD5: " + (new MD5()).run(arrayBuffer, []) + + "\nMD6: " + (new MD6()).run(str, []) + + "\nSHA0: " + (new SHA0()).run(arrayBuffer, []) + + "\nSHA1: " + (new SHA1()).run(arrayBuffer, []) + + "\nSHA2 224: " + (new SHA2()).run(arrayBuffer, ["224"]) + + "\nSHA2 256: " + (new SHA2()).run(arrayBuffer, ["256"]) + + "\nSHA2 384: " + (new SHA2()).run(arrayBuffer, ["384"]) + + "\nSHA2 512: " + (new SHA2()).run(arrayBuffer, ["512"]) + + "\nSHA3 224: " + (new SHA3()).run(arrayBuffer, ["224"]) + + "\nSHA3 256: " + (new SHA3()).run(arrayBuffer, ["256"]) + + "\nSHA3 384: " + (new SHA3()).run(arrayBuffer, ["384"]) + + "\nSHA3 512: " + (new SHA3()).run(arrayBuffer, ["512"]) + + "\nKeccak 224: " + (new Keccak()).run(arrayBuffer, ["224"]) + + "\nKeccak 256: " + (new Keccak()).run(arrayBuffer, ["256"]) + + "\nKeccak 384: " + (new Keccak()).run(arrayBuffer, ["384"]) + + "\nKeccak 512: " + (new Keccak()).run(arrayBuffer, ["512"]) + + "\nShake 128: " + (new Shake()).run(arrayBuffer, ["128", 256]) + + "\nShake 256: " + (new Shake()).run(arrayBuffer, ["256", 512]) + + "\nRIPEMD-128: " + (new RIPEMD()).run(arrayBuffer, ["128"]) + + "\nRIPEMD-160: " + (new RIPEMD()).run(arrayBuffer, ["160"]) + + "\nRIPEMD-256: " + (new RIPEMD()).run(arrayBuffer, ["256"]) + + "\nRIPEMD-320: " + (new RIPEMD()).run(arrayBuffer, ["320"]) + + "\nHAS-160: " + (new HAS160()).run(arrayBuffer, []) + + "\nWhirlpool-0: " + (new Whirlpool()).run(arrayBuffer, ["Whirlpool-0"]) + + "\nWhirlpool-T: " + (new Whirlpool()).run(arrayBuffer, ["Whirlpool-T"]) + + "\nWhirlpool: " + (new Whirlpool()).run(arrayBuffer, ["Whirlpool"]) + + "\nBLAKE2b-128: " + (new BLAKE2b).run(arrayBuffer, ["128", "Hex", {string: "", option: "UTF8"}]) + + "\nBLAKE2b-160: " + (new BLAKE2b).run(arrayBuffer, ["160", "Hex", {string: "", option: "UTF8"}]) + + "\nBLAKE2b-256: " + (new BLAKE2b).run(arrayBuffer, ["256", "Hex", {string: "", option: "UTF8"}]) + + "\nBLAKE2b-384: " + (new BLAKE2b).run(arrayBuffer, ["384", "Hex", {string: "", option: "UTF8"}]) + + "\nBLAKE2b-512: " + (new BLAKE2b).run(arrayBuffer, ["512", "Hex", {string: "", option: "UTF8"}]) + + "\nBLAKE2s-128: " + (new BLAKE2s).run(arrayBuffer, ["128", "Hex", {string: "", option: "UTF8"}]) + + "\nBLAKE2s-160: " + (new BLAKE2s).run(arrayBuffer, ["160", "Hex", {string: "", option: "UTF8"}]) + + "\nBLAKE2s-256: " + (new BLAKE2s).run(arrayBuffer, ["256", "Hex", {string: "", option: "UTF8"}]) + + "\nStreebog-256: " + (new Streebog).run(arrayBuffer, ["256"]) + + "\nStreebog-512: " + (new Streebog).run(arrayBuffer, ["512"]) + + "\nGOST: " + (new GOSTHash).run(arrayBuffer, ["D-A"]) + + "\nSSDEEP: " + (new SSDEEP()).run(str) + + "\nCTPH: " + (new CTPH()).run(str) + "\n\nChecksums:" + - "\nFletcher-8: " + (new Fletcher8Checksum).run(byteArray, []) + - "\nFletcher-16: " + (new Fletcher16Checksum).run(byteArray, []) + - "\nFletcher-32: " + (new Fletcher32Checksum).run(byteArray, []) + - "\nFletcher-64: " + (new Fletcher64Checksum).run(byteArray, []) + - "\nAdler-32: " + (new Adler32Checksum).run(byteArray, []) + - "\nCRC-16: " + (new CRC16Checksum).run(str, []) + - "\nCRC-32: " + (new CRC32Checksum).run(str, []); + "\nFletcher-8: " + (new Fletcher8Checksum).run(byteArray, []) + + "\nFletcher-16: " + (new Fletcher16Checksum).run(byteArray, []) + + "\nFletcher-32: " + (new Fletcher32Checksum).run(byteArray, []) + + "\nFletcher-64: " + (new Fletcher64Checksum).run(byteArray, []) + + "\nAdler-32: " + (new Adler32Checksum).run(byteArray, []) + + "\nCRC-8: " + (new CRC8Checksum).run(arrayBuffer, ["CRC-8"]) + + "\nCRC-16: " + (new CRC16Checksum).run(arrayBuffer, []) + + "\nCRC-32: " + (new CRC32Checksum).run(arrayBuffer, []); return output; } diff --git a/src/core/operations/GenerateHOTP.mjs b/src/core/operations/GenerateHOTP.mjs index a1a80012..b0ab5f1a 100644 --- a/src/core/operations/GenerateHOTP.mjs +++ b/src/core/operations/GenerateHOTP.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import otp from "otp"; -import ToBase32 from "./ToBase32"; +import ToBase32 from "./ToBase32.mjs"; /** * Generate HOTP operation @@ -23,7 +23,7 @@ class GenerateHOTP extends Operation { this.module = "Default"; this.description = "The HMAC-based One-Time Password algorithm (HOTP) is an algorithm that computes a one-time password from a shared secret key and an incrementing counter. It has been adopted as Internet Engineering Task Force standard RFC 4226, is the cornerstone of Initiative For Open Authentication (OATH), and is used in a number of two-factor authentication systems.

Enter the secret as the input or leave it blank for a random secret to be generated."; this.infoURL = "https://wikipedia.org/wiki/HMAC-based_One-time_Password_algorithm"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { @@ -50,7 +50,7 @@ class GenerateHOTP extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ diff --git a/src/core/operations/GenerateLoremIpsum.mjs b/src/core/operations/GenerateLoremIpsum.mjs index fb5ecd17..50056b46 100644 --- a/src/core/operations/GenerateLoremIpsum.mjs +++ b/src/core/operations/GenerateLoremIpsum.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { GenerateParagraphs, GenerateSentences, GenerateWords, GenerateBytes } from "../lib/LoremIpsum"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { GenerateParagraphs, GenerateSentences, GenerateWords, GenerateBytes } from "../lib/LoremIpsum.mjs"; /** * Generate Lorem Ipsum operation diff --git a/src/core/operations/GeneratePGPKeyPair.mjs b/src/core/operations/GeneratePGPKeyPair.mjs index 74f8a404..3a310d08 100644 --- a/src/core/operations/GeneratePGPKeyPair.mjs +++ b/src/core/operations/GeneratePGPKeyPair.mjs @@ -6,9 +6,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import kbpgp from "kbpgp"; -import { getSubkeySize, ASP } from "../lib/PGP"; +import { getSubkeySize, ASP } from "../lib/PGP.mjs"; import * as es6promisify from "es6-promisify"; const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify; diff --git a/src/core/operations/GenerateQRCode.mjs b/src/core/operations/GenerateQRCode.mjs index ac7e5c5c..080a24da 100644 --- a/src/core/operations/GenerateQRCode.mjs +++ b/src/core/operations/GenerateQRCode.mjs @@ -4,12 +4,12 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import qr from "qr-image"; -import { toBase64 } from "../lib/Base64"; -import { isImage } from "../lib/FileType"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { generateQrCode } from "../lib/QRCode.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; +import { isImage } from "../lib/FileType.mjs"; +import Utils from "../Utils.mjs"; /** * Generate QR Code operation @@ -27,7 +27,7 @@ class GenerateQRCode extends Operation { 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"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -38,12 +38,14 @@ class GenerateQRCode extends Operation { { "name": "Module size (px)", "type": "number", - "value": 5 + "value": 5, + "min": 1 }, { "name": "Margin (num modules)", "type": "number", - "value": 2 + "value": 2, + "min": 0 }, { "name": "Error correction", @@ -57,61 +59,34 @@ class GenerateQRCode extends Operation { /** * @param {string} input * @param {Object[]} args - * @returns {byteArray} + * @returns {ArrayBuffer} */ run(input, args) { const [format, size, margin, errorCorrection] = args; - // Create new QR image from the input data, and convert it to a buffer - 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."); - } - - 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."); - } + return generateQrCode(input, format, size, margin, errorCorrection); } /** * Displays the QR image using HTML for web apps * - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data, args) { - if (!data.length) return ""; - - const [format] = args; - + if (!data.byteLength && !data.length) return ""; + const dataArray = new Uint8Array(data), + [format] = args; if (format === "PNG") { - let dataURI = "data:"; - const mime = isImage(data); - if (mime){ - dataURI += mime + ";"; - } else { - throw new OperationError("Invalid PNG file generated by QR image"); + const type = isImage(dataArray); + if (!type) { + throw new OperationError("Invalid file type."); } - dataURI += "base64," + toBase64(data); - return ``; + return ``; } - return Utils.byteArrayToChars(data); + return Utils.arrayBufferToStr(data); } } diff --git a/src/core/operations/GenerateTOTP.mjs b/src/core/operations/GenerateTOTP.mjs index a71708a2..86fde30c 100644 --- a/src/core/operations/GenerateTOTP.mjs +++ b/src/core/operations/GenerateTOTP.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import otp from "otp"; -import ToBase32 from "./ToBase32"; +import ToBase32 from "./ToBase32.mjs"; /** * Generate TOTP operation @@ -23,7 +23,7 @@ class GenerateTOTP extends Operation { this.module = "Default"; this.description = "The Time-based One-Time Password algorithm (TOTP) is an algorithm that computes a one-time password from a shared secret key and the current time. It has been adopted as Internet Engineering Task Force standard RFC 6238, is the cornerstone of Initiative For Open Authentication (OATH), and is used in a number of two-factor authentication systems. A TOTP is an HOTP where the counter is the current time.

Enter the secret as the input or leave it blank for a random secret to be generated. T0 and T1 are in seconds."; this.infoURL = "https://wikipedia.org/wiki/Time-based_One-time_Password_algorithm"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { @@ -55,7 +55,7 @@ class GenerateTOTP extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ diff --git a/src/core/operations/GenerateUUID.mjs b/src/core/operations/GenerateUUID.mjs index eef32c73..1ee0faba 100644 --- a/src/core/operations/GenerateUUID.mjs +++ b/src/core/operations/GenerateUUID.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import crypto from "crypto"; /** diff --git a/src/core/operations/GenericCodeBeautify.mjs b/src/core/operations/GenericCodeBeautify.mjs index 5078bebc..30e11cb3 100644 --- a/src/core/operations/GenericCodeBeautify.mjs +++ b/src/core/operations/GenericCodeBeautify.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Generic Code Beautify operation diff --git a/src/core/operations/GroupIPAddresses.mjs b/src/core/operations/GroupIPAddresses.mjs index 5e5d97c8..bf7c6db8 100644 --- a/src/core/operations/GroupIPAddresses.mjs +++ b/src/core/operations/GroupIPAddresses.mjs @@ -4,11 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; -import {IP_DELIM_OPTIONS} from "../lib/Delim"; -import {ipv6ToStr, genIpv6Mask, IPV4_REGEX, strToIpv6, ipv4ToStr, IPV6_REGEX, strToIpv4} from "../lib/IP"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {IP_DELIM_OPTIONS} from "../lib/Delim.mjs"; +import {ipv6ToStr, genIpv6Mask, IPV4_REGEX, strToIpv6, ipv4ToStr, IPV6_REGEX, strToIpv4} from "../lib/IP.mjs"; /** * Group IP addresses operation @@ -77,7 +77,7 @@ class GroupIPAddresses extends Operation { ip = strToIpv4(match[1]) >>> 0; network = ip & ipv4Mask; - if (ipv4Networks.hasOwnProperty(network)) { + if (network in ipv4Networks) { ipv4Networks[network].push(ip); } else { ipv4Networks[network] = [ip]; @@ -93,7 +93,7 @@ class GroupIPAddresses extends Operation { networkStr = ipv6ToStr(network, true); - if (ipv6Networks.hasOwnProperty(networkStr)) { + if (networkStr in ipv6Networks) { ipv6Networks[networkStr].push(ip); } else { ipv6Networks[networkStr] = [ip]; diff --git a/src/core/operations/Gunzip.mjs b/src/core/operations/Gunzip.mjs index fe514186..07b1d6c2 100644 --- a/src/core/operations/Gunzip.mjs +++ b/src/core/operations/Gunzip.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min"; +import Operation from "../Operation.mjs"; +import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min.js"; const Zlib = zlibAndGzip.Zlib; diff --git a/src/core/operations/Gzip.mjs b/src/core/operations/Gzip.mjs index e4a6c15f..5f9fa474 100644 --- a/src/core/operations/Gzip.mjs +++ b/src/core/operations/Gzip.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {COMPRESSION_TYPE, ZLIB_COMPRESSION_TYPE_LOOKUP} from "../lib/Zlib"; -import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min"; +import Operation from "../Operation.mjs"; +import {COMPRESSION_TYPE, ZLIB_COMPRESSION_TYPE_LOOKUP} from "../lib/Zlib.mjs"; +import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min.js"; const Zlib = zlibAndGzip.Zlib; diff --git a/src/core/operations/HAS160.mjs b/src/core/operations/HAS160.mjs index ab3db6a2..aa1439ae 100644 --- a/src/core/operations/HAS160.mjs +++ b/src/core/operations/HAS160.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {runHash} from "../lib/Hash"; +import Operation from "../Operation.mjs"; +import {runHash} from "../lib/Hash.mjs"; /** * HAS-160 operation diff --git a/src/core/operations/HMAC.mjs b/src/core/operations/HMAC.mjs index d511febb..cb129692 100644 --- a/src/core/operations/HMAC.mjs +++ b/src/core/operations/HMAC.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import CryptoApi from "crypto-api/src/crypto-api"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import CryptoApi from "crypto-api/src/crypto-api.mjs"; /** * HMAC operation diff --git a/src/core/operations/HTMLToText.mjs b/src/core/operations/HTMLToText.mjs index ac17a78f..ff90572a 100644 --- a/src/core/operations/HTMLToText.mjs +++ b/src/core/operations/HTMLToText.mjs @@ -5,7 +5,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * HTML To Text operation diff --git a/src/core/operations/HTTPRequest.mjs b/src/core/operations/HTTPRequest.mjs index 52029930..7d061f37 100644 --- a/src/core/operations/HTTPRequest.mjs +++ b/src/core/operations/HTTPRequest.mjs @@ -5,8 +5,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * HTTP request operation diff --git a/src/core/operations/HammingDistance.mjs b/src/core/operations/HammingDistance.mjs index 87c5d222..fb60f940 100644 --- a/src/core/operations/HammingDistance.mjs +++ b/src/core/operations/HammingDistance.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {fromHex} from "../lib/Hex"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {fromHex} from "../lib/Hex.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Hamming Distance operation diff --git a/src/core/operations/HaversineDistance.mjs b/src/core/operations/HaversineDistance.mjs index d5c0bf60..1d0d25e0 100644 --- a/src/core/operations/HaversineDistance.mjs +++ b/src/core/operations/HaversineDistance.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * HaversineDistance operation diff --git a/src/core/operations/Head.mjs b/src/core/operations/Head.mjs index 76e17c59..d74c3eea 100644 --- a/src/core/operations/Head.mjs +++ b/src/core/operations/Head.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {INPUT_DELIM_OPTIONS} from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {INPUT_DELIM_OPTIONS} from "../lib/Delim.mjs"; /** * Head operation diff --git a/src/core/operations/HeatmapChart.mjs b/src/core/operations/HeatmapChart.mjs index f04b6881..96636986 100644 --- a/src/core/operations/HeatmapChart.mjs +++ b/src/core/operations/HeatmapChart.mjs @@ -7,11 +7,11 @@ import * as d3temp from "d3"; import * as nodomtemp from "nodom"; -import { getScatterValues, RECORD_DELIMITER_OPTIONS, COLOURS, FIELD_DELIMITER_OPTIONS } from "../lib/Charts"; +import { getScatterValues, RECORD_DELIMITER_OPTIONS, COLOURS, FIELD_DELIMITER_OPTIONS } from "../lib/Charts.mjs"; -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; const d3 = d3temp.default ? d3temp.default : d3temp; const nodom = nodomtemp.default ? nodomtemp.default: nodomtemp; diff --git a/src/core/operations/HexDensityChart.mjs b/src/core/operations/HexDensityChart.mjs index 6414d97a..72cab9fa 100644 --- a/src/core/operations/HexDensityChart.mjs +++ b/src/core/operations/HexDensityChart.mjs @@ -8,10 +8,10 @@ import * as d3temp from "d3"; import * as d3hexbintemp from "d3-hexbin"; import * as nodomtemp from "nodom"; -import { getScatterValues, RECORD_DELIMITER_OPTIONS, COLOURS, FIELD_DELIMITER_OPTIONS } from "../lib/Charts"; +import { getScatterValues, RECORD_DELIMITER_OPTIONS, COLOURS, FIELD_DELIMITER_OPTIONS } from "../lib/Charts.mjs"; -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; const d3 = d3temp.default ? d3temp.default : d3temp; const d3hexbin = d3hexbintemp.default ? d3hexbintemp.default : d3hexbintemp; diff --git a/src/core/operations/HexToObjectIdentifier.mjs b/src/core/operations/HexToObjectIdentifier.mjs index aea5dd24..67971f8d 100644 --- a/src/core/operations/HexToObjectIdentifier.mjs +++ b/src/core/operations/HexToObjectIdentifier.mjs @@ -5,7 +5,7 @@ */ import r from "jsrsasign"; -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Hex to Object Identifier operation diff --git a/src/core/operations/HexToPEM.mjs b/src/core/operations/HexToPEM.mjs index b0845685..8217ffbd 100644 --- a/src/core/operations/HexToPEM.mjs +++ b/src/core/operations/HexToPEM.mjs @@ -5,7 +5,7 @@ */ import r from "jsrsasign"; -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Hex to PEM operation diff --git a/src/core/operations/ImageBrightnessContrast.mjs b/src/core/operations/ImageBrightnessContrast.mjs index 27a30cff..81c001dc 100644 --- a/src/core/operations/ImageBrightnessContrast.mjs +++ b/src/core/operations/ImageBrightnessContrast.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; import { toBase64 } from "../lib/Base64.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; import jimp from "jimp"; /** @@ -25,8 +26,8 @@ class ImageBrightnessContrast extends Operation { this.module = "Image"; this.description = "Adjust the brightness or contrast of an image."; this.infoURL = ""; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -47,36 +48,41 @@ class ImageBrightnessContrast extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ async run(input, args) { const [brightness, contrast] = args; - if (!isImage(input)) { + if (!isImage(new Uint8Array(input))) { throw new OperationError("Invalid file type."); } let image; try { - image = await jimp.read(Buffer.from(input)); + image = await jimp.read(input); } catch (err) { throw new OperationError(`Error loading image. (${err})`); } try { if (brightness !== 0) { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Changing image brightness..."); image.brightness(brightness / 100); } if (contrast !== 0) { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Changing image contrast..."); image.contrast(contrast / 100); } - const imageBuffer = await image.getBufferAsync(jimp.AUTO); - return [...imageBuffer]; + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; } catch (err) { throw new OperationError(`Error adjusting image brightness or contrast. (${err})`); } @@ -84,18 +90,19 @@ class ImageBrightnessContrast extends Operation { /** * Displays the image using HTML for web apps - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/ImageFilter.mjs b/src/core/operations/ImageFilter.mjs index aca34042..8501ab34 100644 --- a/src/core/operations/ImageFilter.mjs +++ b/src/core/operations/ImageFilter.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; import { toBase64 } from "../lib/Base64.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; import jimp from "jimp"; /** @@ -25,8 +26,8 @@ class ImageFilter extends Operation { this.module = "Image"; this.description = "Applies a greyscale or sepia filter to an image."; this.infoURL = ""; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -41,24 +42,24 @@ class ImageFilter extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ async run(input, args) { const [filterType] = args; - if (!isImage(input)){ + if (!isImage(new Uint8Array(input))){ throw new OperationError("Invalid file type."); } let image; try { - image = await jimp.read(Buffer.from(input)); + image = await jimp.read(input); } catch (err) { throw new OperationError(`Error loading image. (${err})`); } try { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Applying " + filterType.toLowerCase() + " filter to image..."); if (filterType === "Greyscale") { image.greyscale(); @@ -66,8 +67,13 @@ class ImageFilter extends Operation { image.sepia(); } - const imageBuffer = await image.getBufferAsync(jimp.AUTO); - return [...imageBuffer]; + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; } catch (err) { throw new OperationError(`Error applying filter to image. (${err})`); } @@ -75,18 +81,19 @@ class ImageFilter extends Operation { /** * Displays the blurred image using HTML for web apps - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/ImageHueSaturationLightness.mjs b/src/core/operations/ImageHueSaturationLightness.mjs index bca73c30..7f5cd8cb 100644 --- a/src/core/operations/ImageHueSaturationLightness.mjs +++ b/src/core/operations/ImageHueSaturationLightness.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; import { toBase64 } from "../lib/Base64.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; import jimp from "jimp"; /** @@ -25,8 +26,8 @@ class ImageHueSaturationLightness extends Operation { this.module = "Image"; this.description = "Adjusts the hue / saturation / lightness (HSL) values of an image."; this.infoURL = ""; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -54,26 +55,26 @@ class ImageHueSaturationLightness extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ async run(input, args) { const [hue, saturation, lightness] = args; - if (!isImage(input)) { + if (!isImage(new Uint8Array(input))) { throw new OperationError("Invalid file type."); } let image; try { - image = await jimp.read(Buffer.from(input)); + image = await jimp.read(input); } catch (err) { throw new OperationError(`Error loading image. (${err})`); } try { if (hue !== 0) { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Changing image hue..."); image.colour([ { @@ -83,7 +84,7 @@ class ImageHueSaturationLightness extends Operation { ]); } if (saturation !== 0) { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Changing image saturation..."); image.colour([ { @@ -93,7 +94,7 @@ class ImageHueSaturationLightness extends Operation { ]); } if (lightness !== 0) { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Changing image lightness..."); image.colour([ { @@ -102,8 +103,14 @@ class ImageHueSaturationLightness extends Operation { } ]); } - const imageBuffer = await image.getBufferAsync(jimp.AUTO); - return [...imageBuffer]; + + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; } catch (err) { throw new OperationError(`Error adjusting image hue / saturation / lightness. (${err})`); } @@ -111,18 +118,19 @@ class ImageHueSaturationLightness extends Operation { /** * Displays the image using HTML for web apps - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/ImageOpacity.mjs b/src/core/operations/ImageOpacity.mjs index 999ad176..cc717d58 100644 --- a/src/core/operations/ImageOpacity.mjs +++ b/src/core/operations/ImageOpacity.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; import { toBase64 } from "../lib/Base64.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; import jimp from "jimp"; /** @@ -25,8 +26,8 @@ class ImageOpacity extends Operation { this.module = "Image"; this.description = "Adjust the opacity of an image."; this.infoURL = ""; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -40,29 +41,34 @@ class ImageOpacity extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ async run(input, args) { const [opacity] = args; - if (!isImage(input)) { + if (!isImage(new Uint8Array(input))) { throw new OperationError("Invalid file type."); } let image; try { - image = await jimp.read(Buffer.from(input)); + image = await jimp.read(input); } catch (err) { throw new OperationError(`Error loading image. (${err})`); } try { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Changing image opacity..."); image.opacity(opacity / 100); - const imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); - return [...imageBuffer]; + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; } catch (err) { throw new OperationError(`Error changing image opacity. (${err})`); } @@ -70,18 +76,19 @@ class ImageOpacity extends Operation { /** * Displays the image using HTML for web apps - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/IndexOfCoincidence.mjs b/src/core/operations/IndexOfCoincidence.mjs new file mode 100644 index 00000000..f99c2f23 --- /dev/null +++ b/src/core/operations/IndexOfCoincidence.mjs @@ -0,0 +1,107 @@ +/** + * @author George O [georgeomnet+cyberchef@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; + +/** + * Index of Coincidence operation + */ +class IndexOfCoincidence extends Operation { + + /** + * IndexOfCoincidence constructor + */ + constructor() { + super(); + + this.name = "Index of Coincidence"; + this.module = "Default"; + this.description = "Index of Coincidence (IC) is the probability of two randomly selected characters being the same. This can be used to determine whether text is readable or random, with English text having an IC of around 0.066. IC can therefore be a sound method to automate frequency analysis."; + this.infoURL = "https://wikipedia.org/wiki/Index_of_coincidence"; + this.inputType = "string"; + this.outputType = "number"; + this.presentType = "html"; + this.args = []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {number} + */ + run(input, args) { + const text = input.toLowerCase().replace(/[^a-z]/g, ""), + frequencies = new Array(26).fill(0), + alphabet = Utils.expandAlphRange("a-z"); + let coincidence = 0.00, + density = 0.00, + result = 0.00, + i; + + for (i=0; i < alphabet.length; i++) { + frequencies[i] = text.count(alphabet[i]); + } + + for (i=0; i < frequencies.length; i++) { + coincidence += frequencies[i] * (frequencies[i] - 1); + } + + density = frequencies.sum(); + + // Ensure that we don't divide by 0 + if (density < 2) density = 2; + + result = coincidence / (density * (density - 1)); + + return result; + } + + /** + * Displays the IC as a scale bar for web apps. + * + * @param {number} ic + * @returns {html} + */ + present(ic) { + return `Index of Coincidence: ${ic} +Normalized: ${ic * 26} +

+- 0 represents complete randomness (all characters are unique), whereas 1 represents no randomness (all characters are identical). +- English text generally has an IC of between 0.67 to 0.78. +- 'Random' text is determined by the probability that each letter occurs the same number of times as another. + +The graph shows the IC of the input data. A low IC generally means that the text is random, compressed or encrypted. + + + `; + } + +} + +export default IndexOfCoincidence; diff --git a/src/core/operations/InvertImage.mjs b/src/core/operations/InvertImage.mjs index ed97523f..22830701 100644 --- a/src/core/operations/InvertImage.mjs +++ b/src/core/operations/InvertImage.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; -import { toBase64 } from "../lib/Base64"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; import jimp from "jimp"; /** @@ -25,34 +26,40 @@ class InvertImage extends Operation { this.module = "Image"; this.description = "Invert the colours of an image."; this.infoURL = ""; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ async run(input, args) { - if (!isImage(input)) { + if (!isImage(new Uint8Array(input))) { throw new OperationError("Invalid input file format."); } let image; try { - image = await jimp.read(Buffer.from(input)); + image = await jimp.read(input); } catch (err) { throw new OperationError(`Error loading image. (${err})`); } try { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Inverting image..."); image.invert(); - const imageBuffer = await image.getBufferAsync(jimp.AUTO); - return [...imageBuffer]; + + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; } catch (err) { throw new OperationError(`Error inverting image. (${err})`); } @@ -60,18 +67,19 @@ class InvertImage extends Operation { /** * Displays the inverted image using HTML for web apps - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/JPathExpression.mjs b/src/core/operations/JPathExpression.mjs index df99fc56..328fc83f 100644 --- a/src/core/operations/JPathExpression.mjs +++ b/src/core/operations/JPathExpression.mjs @@ -5,8 +5,8 @@ */ import jpath from "jsonpath"; -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * JPath expression operation diff --git a/src/core/operations/JSONBeautify.mjs b/src/core/operations/JSONBeautify.mjs index a2b2dfdd..f53d86e6 100644 --- a/src/core/operations/JSONBeautify.mjs +++ b/src/core/operations/JSONBeautify.mjs @@ -6,7 +6,7 @@ */ import vkbeautify from "vkbeautify"; -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * JSON Beautify operation diff --git a/src/core/operations/JSONMinify.mjs b/src/core/operations/JSONMinify.mjs index ca594397..7f0c6f2b 100644 --- a/src/core/operations/JSONMinify.mjs +++ b/src/core/operations/JSONMinify.mjs @@ -5,7 +5,7 @@ */ import vkbeautify from "vkbeautify"; -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * JSON Minify operation diff --git a/src/core/operations/JSONToCSV.mjs b/src/core/operations/JSONToCSV.mjs index c3d078e3..e846f164 100644 --- a/src/core/operations/JSONToCSV.mjs +++ b/src/core/operations/JSONToCSV.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * JSON to CSV operation @@ -51,6 +51,10 @@ class JSONToCSV extends Operation { this.rowDelim = rowDelim; const self = this; + if (!(input instanceof Array)) { + input = [input]; + } + try { // If the JSON is an array of arrays, this is easy if (input[0] instanceof Array) { @@ -89,6 +93,8 @@ class JSONToCSV extends Operation { * @returns {string} */ escapeCellContents(data) { + if (typeof data === "number") data = data.toString(); + // Double quotes should be doubled up data = data.replace(/"/g, '""'); diff --git a/src/core/operations/JWTDecode.mjs b/src/core/operations/JWTDecode.mjs index 2166a447..07f19477 100644 --- a/src/core/operations/JWTDecode.mjs +++ b/src/core/operations/JWTDecode.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import jwt from "jsonwebtoken"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * JWT Decode operation diff --git a/src/core/operations/JWTSign.mjs b/src/core/operations/JWTSign.mjs index b3f79b57..d62eb6f6 100644 --- a/src/core/operations/JWTSign.mjs +++ b/src/core/operations/JWTSign.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import jwt from "jsonwebtoken"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * JWT Sign operation diff --git a/src/core/operations/JWTVerify.mjs b/src/core/operations/JWTVerify.mjs index 651e7662..996ac2e3 100644 --- a/src/core/operations/JWTVerify.mjs +++ b/src/core/operations/JWTVerify.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import jwt from "jsonwebtoken"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * JWT Verify operation @@ -50,7 +50,7 @@ class JWTVerify extends Operation { "none" ]}); - if (verified.hasOwnProperty("name") && verified.name === "JsonWebTokenError") { + if (Object.prototype.hasOwnProperty.call(verified, "name") && verified.name === "JsonWebTokenError") { throw new OperationError(verified.message); } diff --git a/src/core/operations/JavaScriptBeautify.mjs b/src/core/operations/JavaScriptBeautify.mjs index a4bf326b..94575fcb 100644 --- a/src/core/operations/JavaScriptBeautify.mjs +++ b/src/core/operations/JavaScriptBeautify.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; import escodegen from "escodegen"; import * as esprima from "esprima"; diff --git a/src/core/operations/JavaScriptMinify.mjs b/src/core/operations/JavaScriptMinify.mjs index 9841d5bb..b75aa857 100644 --- a/src/core/operations/JavaScriptMinify.mjs +++ b/src/core/operations/JavaScriptMinify.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import * as esprima from "esprima"; import escodegen from "escodegen"; import esmangle from "esmangle"; diff --git a/src/core/operations/JavaScriptParser.mjs b/src/core/operations/JavaScriptParser.mjs index 61d7cf50..2fb13891 100644 --- a/src/core/operations/JavaScriptParser.mjs +++ b/src/core/operations/JavaScriptParser.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import * as esprima from "esprima"; /** diff --git a/src/core/operations/Jump.mjs b/src/core/operations/Jump.mjs index 30fca5a0..f442faa0 100644 --- a/src/core/operations/Jump.mjs +++ b/src/core/operations/Jump.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { getLabelIndex } from "../lib/FlowControl"; +import Operation from "../Operation.mjs"; +import { getLabelIndex } from "../lib/FlowControl.mjs"; /** * Jump operation diff --git a/src/core/operations/Keccak.mjs b/src/core/operations/Keccak.mjs index 0c930589..151a4e82 100644 --- a/src/core/operations/Keccak.mjs +++ b/src/core/operations/Keccak.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import JSSHA3 from "js-sha3"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * Keccak operation diff --git a/src/core/operations/Label.mjs b/src/core/operations/Label.mjs index 1444f3ac..fb13d803 100644 --- a/src/core/operations/Label.mjs +++ b/src/core/operations/Label.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Label operation. For use with Jump and Conditional Jump. diff --git a/src/core/operations/MD2.mjs b/src/core/operations/MD2.mjs index dfe2c7a3..ecfa699c 100644 --- a/src/core/operations/MD2.mjs +++ b/src/core/operations/MD2.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {runHash} from "../lib/Hash"; +import Operation from "../Operation.mjs"; +import {runHash} from "../lib/Hash.mjs"; /** * MD2 operation diff --git a/src/core/operations/MD4.mjs b/src/core/operations/MD4.mjs index 7872c7b8..381528cc 100644 --- a/src/core/operations/MD4.mjs +++ b/src/core/operations/MD4.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {runHash} from "../lib/Hash"; +import Operation from "../Operation.mjs"; +import {runHash} from "../lib/Hash.mjs"; /** * MD4 operation diff --git a/src/core/operations/MD5.mjs b/src/core/operations/MD5.mjs index 96de7108..f55edaf5 100644 --- a/src/core/operations/MD5.mjs +++ b/src/core/operations/MD5.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {runHash} from "../lib/Hash"; +import Operation from "../Operation.mjs"; +import {runHash} from "../lib/Hash.mjs"; /** * MD5 operation diff --git a/src/core/operations/MD6.mjs b/src/core/operations/MD6.mjs index 0e53a1b0..0ab13ffe 100644 --- a/src/core/operations/MD6.mjs +++ b/src/core/operations/MD6.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; import NodeMD6 from "node-md6"; /** diff --git a/src/core/operations/Magic.mjs b/src/core/operations/Magic.mjs index 1555ebad..d5357d95 100644 --- a/src/core/operations/Magic.mjs +++ b/src/core/operations/Magic.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import Dish from "../Dish"; -import MagicLib from "../lib/Magic"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import Dish from "../Dish.mjs"; +import MagicLib from "../lib/Magic.mjs"; /** * Magic operation diff --git a/src/core/operations/Mean.mjs b/src/core/operations/Mean.mjs index a7b110ce..ee826201 100644 --- a/src/core/operations/Mean.mjs +++ b/src/core/operations/Mean.mjs @@ -5,9 +5,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { mean, createNumArray } from "../lib/Arithmetic"; -import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import { mean, createNumArray } from "../lib/Arithmetic.mjs"; +import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim.mjs"; import BigNumber from "bignumber.js"; /** diff --git a/src/core/operations/Median.mjs b/src/core/operations/Median.mjs index 7101a403..4ec9eceb 100644 --- a/src/core/operations/Median.mjs +++ b/src/core/operations/Median.mjs @@ -6,9 +6,9 @@ */ import BigNumber from "bignumber.js"; -import Operation from "../Operation"; -import { median, createNumArray } from "../lib/Arithmetic"; -import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import { median, createNumArray } from "../lib/Arithmetic.mjs"; +import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim.mjs"; /** * Median operation diff --git a/src/core/operations/Merge.mjs b/src/core/operations/Merge.mjs index 462660c4..07123aee 100644 --- a/src/core/operations/Merge.mjs +++ b/src/core/operations/Merge.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Merge operation diff --git a/src/core/operations/MicrosoftScriptDecoder.mjs b/src/core/operations/MicrosoftScriptDecoder.mjs index bb475ffc..460573b2 100644 --- a/src/core/operations/MicrosoftScriptDecoder.mjs +++ b/src/core/operations/MicrosoftScriptDecoder.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Microsoft Script Decoder operation diff --git a/src/core/operations/MultipleBombe.mjs b/src/core/operations/MultipleBombe.mjs index b6a48872..99725904 100644 --- a/src/core/operations/MultipleBombe.mjs +++ b/src/core/operations/MultipleBombe.mjs @@ -7,10 +7,12 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import {BombeMachine} from "../lib/Bombe"; -import {ROTORS, ROTORS_FOURTH, REFLECTORS, Reflector} from "../lib/Enigma"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { BombeMachine } from "../lib/Bombe.mjs"; +import { ROTORS, ROTORS_FOURTH, REFLECTORS, Reflector } from "../lib/Enigma.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; + /** * Convenience method for flattening the preset ROTORS object into a newline-separated string. @@ -222,7 +224,7 @@ class MultipleBombe extends Operation { crib = crib.replace(/[^A-Za-z]/g, "").toUpperCase(); const ciphertext = input.slice(offset); let update; - if (ENVIRONMENT_IS_WORKER()) { + if (isWorkerEnvironment()) { update = this.updateStatus; } else { update = undefined; diff --git a/src/core/operations/Multiply.mjs b/src/core/operations/Multiply.mjs index 825078e2..9f1666e8 100644 --- a/src/core/operations/Multiply.mjs +++ b/src/core/operations/Multiply.mjs @@ -6,9 +6,9 @@ */ import BigNumber from "bignumber.js"; -import Operation from "../Operation"; -import { multi, createNumArray } from "../lib/Arithmetic"; -import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import { multi, createNumArray } from "../lib/Arithmetic.mjs"; +import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim.mjs"; /** diff --git a/src/core/operations/NOT.mjs b/src/core/operations/NOT.mjs index e7f00393..46fc1b8c 100644 --- a/src/core/operations/NOT.mjs +++ b/src/core/operations/NOT.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { bitOp, not } from "../lib/BitwiseOp"; +import Operation from "../Operation.mjs"; +import { bitOp, not } from "../lib/BitwiseOp.mjs"; /** * NOT operation @@ -22,17 +22,18 @@ class NOT extends Operation { this.module = "Default"; this.description = "Returns the inverse of each byte."; this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#NOT"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "byteArray"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ run(input, args) { + input = new Uint8Array(input); return bitOp(input, null, not); } diff --git a/src/core/operations/NormaliseImage.mjs b/src/core/operations/NormaliseImage.mjs index bb5113a7..c40bbc33 100644 --- a/src/core/operations/NormaliseImage.mjs +++ b/src/core/operations/NormaliseImage.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; -import { toBase64 } from "../lib/Base64"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; import jimp from "jimp"; /** @@ -25,44 +25,59 @@ class NormaliseImage extends Operation { this.module = "Image"; this.description = "Normalise the image colours."; this.infoURL = ""; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType= "html"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ async run(input, args) { - if (!isImage(input)) { + if (!isImage(new Uint8Array(input))) { throw new OperationError("Invalid file type."); } - const image = await jimp.read(Buffer.from(input)); + let image; + try { + image = await jimp.read(input); + } catch (err) { + throw new OperationError(`Error opening image file. (${err})`); + } - image.normalize(); + try { + image.normalize(); - const imageBuffer = await image.getBufferAsync(jimp.AUTO); - return [...imageBuffer]; + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; + } catch (err) { + throw new OperationError(`Error normalising image. (${err})`); + } } /** * Displays the normalised image using HTML for web apps - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/Numberwang.mjs b/src/core/operations/Numberwang.mjs index 96100fbb..7f94e360 100644 --- a/src/core/operations/Numberwang.mjs +++ b/src/core/operations/Numberwang.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Numberwang operation. Remain indoors. diff --git a/src/core/operations/OR.mjs b/src/core/operations/OR.mjs index 30948379..183fb1fe 100644 --- a/src/core/operations/OR.mjs +++ b/src/core/operations/OR.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import { bitOp, or, BITWISE_OP_DELIMS } from "../lib/BitwiseOp"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import { bitOp, or, BITWISE_OP_DELIMS } from "../lib/BitwiseOp.mjs"; /** * OR operation @@ -23,7 +23,7 @@ class OR extends Operation { this.module = "Default"; this.description = "OR the input with the given key.
e.g. fe023da5"; this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#OR"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "byteArray"; this.args = [ { @@ -36,12 +36,13 @@ class OR extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ run(input, args) { const key = Utils.convertToByteArray(args[0].string || "", args[0].option); + input = new Uint8Array(input); return bitOp(input, key, or); } diff --git a/src/core/operations/ObjectIdentifierToHex.mjs b/src/core/operations/ObjectIdentifierToHex.mjs index 90e45ea5..3e78cc03 100644 --- a/src/core/operations/ObjectIdentifierToHex.mjs +++ b/src/core/operations/ObjectIdentifierToHex.mjs @@ -5,7 +5,7 @@ */ import r from "jsrsasign"; -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Object Identifier to Hex operation diff --git a/src/core/operations/OffsetChecker.mjs b/src/core/operations/OffsetChecker.mjs index 07b15d2f..0f66e591 100644 --- a/src/core/operations/OffsetChecker.mjs +++ b/src/core/operations/OffsetChecker.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Offset checker operation diff --git a/src/core/operations/PEMToHex.mjs b/src/core/operations/PEMToHex.mjs index 580b1028..095e2c56 100644 --- a/src/core/operations/PEMToHex.mjs +++ b/src/core/operations/PEMToHex.mjs @@ -5,7 +5,7 @@ */ import r from "jsrsasign"; -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * PEM to Hex operation diff --git a/src/core/operations/PGPDecrypt.mjs b/src/core/operations/PGPDecrypt.mjs index ccf13cd1..d69b88ea 100644 --- a/src/core/operations/PGPDecrypt.mjs +++ b/src/core/operations/PGPDecrypt.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import kbpgp from "kbpgp"; -import { ASP, importPrivateKey } from "../lib/PGP"; -import OperationError from "../errors/OperationError"; +import { ASP, importPrivateKey } from "../lib/PGP.mjs"; +import OperationError from "../errors/OperationError.mjs"; import * as es6promisify from "es6-promisify"; const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify; diff --git a/src/core/operations/PGPDecryptAndVerify.mjs b/src/core/operations/PGPDecryptAndVerify.mjs index 58c61c25..119ccf16 100644 --- a/src/core/operations/PGPDecryptAndVerify.mjs +++ b/src/core/operations/PGPDecryptAndVerify.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import kbpgp from "kbpgp"; -import { ASP, importPrivateKey, importPublicKey } from "../lib/PGP"; -import OperationError from "../errors/OperationError"; +import { ASP, importPrivateKey, importPublicKey } from "../lib/PGP.mjs"; +import OperationError from "../errors/OperationError.mjs"; import * as es6promisify from "es6-promisify"; const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify; @@ -93,7 +93,7 @@ class PGPDecryptAndVerify extends Operation { text += `${signer.username} `; } if (signer.comment) { - text += `${signer.comment} `; + text += `(${signer.comment}) `; } if (signer.email) { text += `<${signer.email}>`; @@ -101,8 +101,9 @@ class PGPDecryptAndVerify extends Operation { text += "\n"; } text += [ + `PGP key ID: ${km.get_pgp_short_key_id()}`, `PGP fingerprint: ${km.get_pgp_fingerprint().toString("hex")}`, - `Signed on ${new Date(ds.sig.hashed_subpackets[0].time * 1000).toUTCString()}`, + `Signed on ${new Date(ds.sig.when_generated() * 1000).toUTCString()}`, "----------------------------------\n" ].join("\n"); text += unboxedLiterals.toString(); diff --git a/src/core/operations/PGPEncrypt.mjs b/src/core/operations/PGPEncrypt.mjs index 5d19de32..2f41cc0b 100644 --- a/src/core/operations/PGPEncrypt.mjs +++ b/src/core/operations/PGPEncrypt.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import kbpgp from "kbpgp"; -import { ASP, importPublicKey } from "../lib/PGP"; -import OperationError from "../errors/OperationError"; +import { ASP, importPublicKey } from "../lib/PGP.mjs"; +import OperationError from "../errors/OperationError.mjs"; import * as es6promisify from "es6-promisify"; const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify; diff --git a/src/core/operations/PGPEncryptAndSign.mjs b/src/core/operations/PGPEncryptAndSign.mjs index d9d19ce3..8fdbe4c3 100644 --- a/src/core/operations/PGPEncryptAndSign.mjs +++ b/src/core/operations/PGPEncryptAndSign.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import kbpgp from "kbpgp"; -import { ASP, importPrivateKey, importPublicKey } from "../lib/PGP"; -import OperationError from "../errors/OperationError"; +import { ASP, importPrivateKey, importPublicKey } from "../lib/PGP.mjs"; +import OperationError from "../errors/OperationError.mjs"; import * as es6promisify from "es6-promisify"; const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify; diff --git a/src/core/operations/PGPVerify.mjs b/src/core/operations/PGPVerify.mjs new file mode 100644 index 00000000..ee346916 --- /dev/null +++ b/src/core/operations/PGPVerify.mjs @@ -0,0 +1,111 @@ +/** + * @author Matt C [me@mitt.dev] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +import kbpgp from "kbpgp"; +import { ASP, importPublicKey } from "../lib/PGP.mjs"; +import * as es6promisify from "es6-promisify"; +const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify; + +/** + * PGP Verify operation + */ +class PGPVerify extends Operation { + + /** + * PGPVerify constructor + */ + constructor() { + super(); + + this.name = "PGP Verify"; + this.module = "PGP"; + this.description = [ + "Input: the ASCII-armoured encrypted PGP message you want to verify.", + "

", + "Argument: the ASCII-armoured PGP public key of the signer", + "

", + "This operation uses PGP to decrypt a clearsigned message.", + "

", + "Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.", + "

", + "This function uses the Keybase implementation of PGP.", + ].join("\n"); + this.infoURL = "https://wikipedia.org/wiki/Pretty_Good_Privacy"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Public key of signer", + "type": "text", + "value": "" + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + async run(input, args) { + const signedMessage = input, + [publicKey] = args, + keyring = new kbpgp.keyring.KeyRing(); + let unboxedLiterals; + + if (!publicKey) throw new OperationError("Enter the public key of the signer."); + const pubKey = await importPublicKey(publicKey); + keyring.add_key_manager(pubKey); + + try { + unboxedLiterals = await promisify(kbpgp.unbox)({ + armored: signedMessage, + keyfetch: keyring, + asp: ASP + }); + const ds = unboxedLiterals[0].get_data_signer(); + if (ds) { + const km = ds.get_key_manager(); + if (km) { + const signer = km.get_userids_mark_primary()[0].components; + let text = "Signed by "; + if (signer.email || signer.username || signer.comment) { + if (signer.username) { + text += `${signer.username} `; + } + if (signer.comment) { + text += `(${signer.comment}) `; + } + if (signer.email) { + text += `<${signer.email}>`; + } + text += "\n"; + } + text += [ + `PGP key ID: ${km.get_pgp_short_key_id()}`, + `PGP fingerprint: ${km.get_pgp_fingerprint().toString("hex")}`, + `Signed on ${new Date(ds.sig.when_generated() * 1000).toUTCString()}`, + "----------------------------------\n" + ].join("\n"); + text += unboxedLiterals.toString(); + return text.trim(); + } else { + throw new OperationError("Could not identify a key manager."); + } + } else { + throw new OperationError("The data does not appear to be signed."); + } + } catch (err) { + throw new OperationError(`Couldn't verify message: ${err}`); + } + } + +} + +export default PGPVerify; diff --git a/src/core/operations/PHPDeserialize.mjs b/src/core/operations/PHPDeserialize.mjs index 760e4e05..904ced9e 100644 --- a/src/core/operations/PHPDeserialize.mjs +++ b/src/core/operations/PHPDeserialize.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * PHP Deserialize operation diff --git a/src/core/operations/PadLines.mjs b/src/core/operations/PadLines.mjs index e9e2b45a..c1464cce 100644 --- a/src/core/operations/PadLines.mjs +++ b/src/core/operations/PadLines.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Pad lines operation diff --git a/src/core/operations/ParseASN1HexString.mjs b/src/core/operations/ParseASN1HexString.mjs index 92c070a4..a19ca70c 100644 --- a/src/core/operations/ParseASN1HexString.mjs +++ b/src/core/operations/ParseASN1HexString.mjs @@ -5,7 +5,7 @@ */ import r from "jsrsasign"; -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Parse ASN.1 hex string operation diff --git a/src/core/operations/ParseColourCode.mjs b/src/core/operations/ParseColourCode.mjs index c97f9b89..5136f1a7 100644 --- a/src/core/operations/ParseColourCode.mjs +++ b/src/core/operations/ParseColourCode.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Parse colour code operation @@ -96,7 +96,7 @@ class ParseColourCode extends Operation { cmyk = "cmyk(" + c + ", " + m + ", " + y + ", " + k + ")"; // Generate output - return `
+ return `
Hex: ${hex} RGB: ${rgb} RGBA: ${rgba} @@ -109,12 +109,12 @@ CMYK: ${cmyk} color: '${rgba}', container: true, inline: true, - }).on('changeColor', function(e) { - var color = e.color.toRGB(); - document.getElementById('input-text').value = 'rgba(' + - color.r + ', ' + color.g + ', ' + color.b + ', ' + color.a + ')'; + useAlpha: true + }).on('colorpickerChange', function(e) { + var color = e.color.string('rgba'); + document.getElementById('input-text').value = color; window.app.autoBake(); - }).children(".colorpicker").removeClass('dropdown-menu'); + }); `; } diff --git a/src/core/operations/ParseDateTime.mjs b/src/core/operations/ParseDateTime.mjs index 82af1c32..b8f2e08d 100644 --- a/src/core/operations/ParseDateTime.mjs +++ b/src/core/operations/ParseDateTime.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import moment from "moment-timezone"; -import {DATETIME_FORMATS, FORMAT_EXAMPLES} from "../lib/DateTime"; +import {DATETIME_FORMATS, FORMAT_EXAMPLES} from "../lib/DateTime.mjs"; /** * Parse DateTime operation diff --git a/src/core/operations/ParseIPRange.mjs b/src/core/operations/ParseIPRange.mjs index 38bcc222..2c59c015 100644 --- a/src/core/operations/ParseIPRange.mjs +++ b/src/core/operations/ParseIPRange.mjs @@ -5,9 +5,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import {ipv4CidrRange, ipv4HyphenatedRange, ipv4ListedRange, ipv6CidrRange, ipv6HyphenatedRange, ipv6ListedRange} from "../lib/IP"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {ipv4CidrRange, ipv4HyphenatedRange, ipv4ListedRange, ipv6CidrRange, ipv6HyphenatedRange, ipv6ListedRange} from "../lib/IP.mjs"; /** * Parse IP range operation diff --git a/src/core/operations/ParseIPv4Header.mjs b/src/core/operations/ParseIPv4Header.mjs index 06114329..304ba0c0 100644 --- a/src/core/operations/ParseIPv4Header.mjs +++ b/src/core/operations/ParseIPv4Header.mjs @@ -4,12 +4,12 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; -import {fromHex, toHex} from "../lib/Hex"; -import {ipv4ToStr, protocolLookup} from "../lib/IP"; -import TCPIPChecksum from "./TCPIPChecksum"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {fromHex, toHex} from "../lib/Hex.mjs"; +import {ipv4ToStr, protocolLookup} from "../lib/IP.mjs"; +import TCPIPChecksum from "./TCPIPChecksum.mjs"; /** * Parse IPv4 header operation diff --git a/src/core/operations/ParseIPv6Address.mjs b/src/core/operations/ParseIPv6Address.mjs index 3cf9f2dd..78df0b19 100644 --- a/src/core/operations/ParseIPv6Address.mjs +++ b/src/core/operations/ParseIPv6Address.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; -import {strToIpv6, ipv6ToStr, ipv4ToStr, IPV6_REGEX} from "../lib/IP"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {strToIpv6, ipv6ToStr, ipv4ToStr, IPV6_REGEX} from "../lib/IP.mjs"; import BigNumber from "bignumber.js"; /** diff --git a/src/core/operations/ParseQRCode.mjs b/src/core/operations/ParseQRCode.mjs index ef7af6d7..13849e71 100644 --- a/src/core/operations/ParseQRCode.mjs +++ b/src/core/operations/ParseQRCode.mjs @@ -4,11 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; -import jsqr from "jsqr"; -import jimp from "jimp"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; +import { parseQrCode } from "../lib/QRCode.mjs"; /** * Parse QR Code operation @@ -25,7 +24,7 @@ class ParseQRCode extends Operation { this.module = "Image"; 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.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { @@ -34,69 +33,28 @@ class ParseQRCode extends Operation { "value": false } ]; + this.patterns = [ + { + "match": "^(?:\\xff\\xd8\\xff|\\x89\\x50\\x4e\\x47|\\x47\\x49\\x46|.{8}\\x57\\x45\\x42\\x50|\\x42\\x4d)", + "flags": "", + "args": [false], + "useful": true + } + ]; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ async run(input, args) { const [normalise] = args; - // Make sure that the input is an image - if (!isImage(input)) throw new OperationError("Invalid file type."); - - 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 - image = 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.")); - }); - }); + if (!isImage(new Uint8Array(input))) { + throw new OperationError("Invalid file type."); } - - if (image instanceof OperationError) { - throw image; - } - - return new Promise((resolve, reject) => { - jimp.read(Buffer.from(image)) - .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 image file.")); - } - }) - .catch(err => { - reject(new OperationError("Error reading the image file.")); - }); - }); - + return await parseQrCode(input, normalise); } } diff --git a/src/core/operations/ParseTLV.mjs b/src/core/operations/ParseTLV.mjs index a87144a8..d4c4e11c 100644 --- a/src/core/operations/ParseTLV.mjs +++ b/src/core/operations/ParseTLV.mjs @@ -5,9 +5,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import TLVParser from "../lib/TLVParser"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import TLVParser from "../lib/TLVParser.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Parse TLV operation @@ -24,7 +24,7 @@ class ParseTLV extends Operation { this.module = "Default"; this.description = "Converts a Type-Length-Value (TLV) encoded string into a JSON object. Can optionally include a Key / Type entry.

Tags: Key-Length-Value, KLV, Length-Value, LV"; this.infoURL = "https://wikipedia.org/wiki/Type-length-value"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "JSON"; this.args = [ { @@ -46,12 +46,13 @@ class ParseTLV extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { const [bytesInKey, bytesInLength, basicEncodingRules] = args; + input = new Uint8Array(input); if (bytesInKey <= 0 && bytesInLength <= 0) throw new OperationError("Type or Length size must be greater than 0"); diff --git a/src/core/operations/ParseUNIXFilePermissions.mjs b/src/core/operations/ParseUNIXFilePermissions.mjs index 8a0eac7a..528e94b5 100644 --- a/src/core/operations/ParseUNIXFilePermissions.mjs +++ b/src/core/operations/ParseUNIXFilePermissions.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Parse UNIX file permissions operation diff --git a/src/core/operations/ParseURI.mjs b/src/core/operations/ParseURI.mjs index d86cb061..17ca90db 100644 --- a/src/core/operations/ParseURI.mjs +++ b/src/core/operations/ParseURI.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import url from "url"; /** diff --git a/src/core/operations/ParseUserAgent.mjs b/src/core/operations/ParseUserAgent.mjs index 03ccc86e..2c0d2c56 100644 --- a/src/core/operations/ParseUserAgent.mjs +++ b/src/core/operations/ParseUserAgent.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import UAParser from "ua-parser-js"; /** diff --git a/src/core/operations/ParseX509Certificate.mjs b/src/core/operations/ParseX509Certificate.mjs index a57f661e..0a1a162e 100644 --- a/src/core/operations/ParseX509Certificate.mjs +++ b/src/core/operations/ParseX509Certificate.mjs @@ -5,11 +5,11 @@ */ import r from "jsrsasign"; -import { fromBase64 } from "../lib/Base64"; -import { toHex } from "../lib/Hex"; -import { formatByteStr, formatDnStr } from "../lib/PublicKey"; -import Operation from "../Operation"; -import Utils from "../Utils"; +import { fromBase64 } from "../lib/Base64.mjs"; +import { toHex } from "../lib/Hex.mjs"; +import { formatByteStr, formatDnStr } from "../lib/PublicKey.mjs"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * Parse X.509 certificate operation diff --git a/src/core/operations/PlayMedia.mjs b/src/core/operations/PlayMedia.mjs index 98b7d088..f16684c7 100644 --- a/src/core/operations/PlayMedia.mjs +++ b/src/core/operations/PlayMedia.mjs @@ -4,12 +4,12 @@ * @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 { isType, detectFileType } from "../lib/FileType"; +import { fromBase64, toBase64 } from "../lib/Base64.mjs"; +import { fromHex } from "../lib/Hex.mjs"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; +import { isType, detectFileType } from "../lib/FileType.mjs"; /** * PlayMedia operation diff --git a/src/core/operations/PowerSet.mjs b/src/core/operations/PowerSet.mjs index 6866c254..a05dd783 100644 --- a/src/core/operations/PowerSet.mjs +++ b/src/core/operations/PowerSet.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Power Set operation diff --git a/src/core/operations/ProtobufDecode.mjs b/src/core/operations/ProtobufDecode.mjs index ec520d05..8470bdb7 100644 --- a/src/core/operations/ProtobufDecode.mjs +++ b/src/core/operations/ProtobufDecode.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import Protobuf from "../lib/Protobuf"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Protobuf from "../lib/Protobuf.mjs"; /** * Protobuf Decode operation @@ -23,17 +23,18 @@ class ProtobufDecode extends Operation { this.module = "Default"; this.description = "Decodes any Protobuf encoded data to a JSON representation of the data using the field number as the field key."; this.infoURL = "https://wikipedia.org/wiki/Protocol_Buffers"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "JSON"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {JSON} */ run(input, args) { + input = new Uint8Array(input); try { return Protobuf.decode(input); } catch (err) { diff --git a/src/core/operations/PseudoRandomNumberGenerator.mjs b/src/core/operations/PseudoRandomNumberGenerator.mjs index 6fdebefd..d9764aba 100644 --- a/src/core/operations/PseudoRandomNumberGenerator.mjs +++ b/src/core/operations/PseudoRandomNumberGenerator.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; import forge from "node-forge/dist/forge.min.js"; import BigNumber from "bignumber.js"; +import { isWorkerEnvironment } from "../Utils.mjs"; /** * Pseudo-Random Number Generator operation @@ -50,7 +51,7 @@ class PseudoRandomNumberGenerator extends Operation { let bytes; - if (ENVIRONMENT_IS_WORKER() && self.crypto) { + if (isWorkerEnvironment() && self.crypto) { bytes = self.crypto.getRandomValues(new Uint8Array(numBytes)); bytes = Utils.arrayBufferToStr(bytes.buffer); } else { diff --git a/src/core/operations/RC2Decrypt.mjs b/src/core/operations/RC2Decrypt.mjs index b0302cd7..78925d4c 100644 --- a/src/core/operations/RC2Decrypt.mjs +++ b/src/core/operations/RC2Decrypt.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; import forge from "node-forge/dist/forge.min.js"; /** diff --git a/src/core/operations/RC2Encrypt.mjs b/src/core/operations/RC2Encrypt.mjs index 5c59c1ea..b917fdc2 100644 --- a/src/core/operations/RC2Encrypt.mjs +++ b/src/core/operations/RC2Encrypt.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; import forge from "node-forge/dist/forge.min.js"; diff --git a/src/core/operations/RC4.mjs b/src/core/operations/RC4.mjs index 1446c972..183db742 100644 --- a/src/core/operations/RC4.mjs +++ b/src/core/operations/RC4.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import CryptoJS from "crypto-js"; -import { format } from "../lib/Ciphers"; +import { format } from "../lib/Ciphers.mjs"; /** * RC4 operation diff --git a/src/core/operations/RC4Drop.mjs b/src/core/operations/RC4Drop.mjs index 243d3a7a..3d87f4cb 100644 --- a/src/core/operations/RC4Drop.mjs +++ b/src/core/operations/RC4Drop.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import { format } from "../lib/Ciphers"; +import Operation from "../Operation.mjs"; +import { format } from "../lib/Ciphers.mjs"; import CryptoJS from "crypto-js"; /** diff --git a/src/core/operations/RIPEMD.mjs b/src/core/operations/RIPEMD.mjs index 00b613de..f326b694 100644 --- a/src/core/operations/RIPEMD.mjs +++ b/src/core/operations/RIPEMD.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {runHash} from "../lib/Hash"; +import Operation from "../Operation.mjs"; +import {runHash} from "../lib/Hash.mjs"; /** * RIPEMD operation diff --git a/src/core/operations/ROT13.mjs b/src/core/operations/ROT13.mjs index f1dda5a8..c36766aa 100644 --- a/src/core/operations/ROT13.mjs +++ b/src/core/operations/ROT13.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** diff --git a/src/core/operations/ROT47.mjs b/src/core/operations/ROT47.mjs index 1219b349..39bf79a2 100644 --- a/src/core/operations/ROT47.mjs +++ b/src/core/operations/ROT47.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** diff --git a/src/core/operations/RawDeflate.mjs b/src/core/operations/RawDeflate.mjs index c1d6dd53..0df243f4 100644 --- a/src/core/operations/RawDeflate.mjs +++ b/src/core/operations/RawDeflate.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {COMPRESSION_TYPE} from "../lib/Zlib"; -import rawdeflate from "zlibjs/bin/rawdeflate.min"; +import Operation from "../Operation.mjs"; +import {COMPRESSION_TYPE} from "../lib/Zlib.mjs"; +import rawdeflate from "zlibjs/bin/rawdeflate.min.js"; const Zlib = rawdeflate.Zlib; diff --git a/src/core/operations/RawInflate.mjs b/src/core/operations/RawInflate.mjs index 99066d79..d676ff1e 100644 --- a/src/core/operations/RawInflate.mjs +++ b/src/core/operations/RawInflate.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {INFLATE_BUFFER_TYPE} from "../lib/Zlib"; -import rawinflate from "zlibjs/bin/rawinflate.min"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import {INFLATE_BUFFER_TYPE} from "../lib/Zlib.mjs"; +import rawinflate from "zlibjs/bin/rawinflate.min.js"; +import OperationError from "../errors/OperationError.mjs"; const Zlib = rawinflate.Zlib; diff --git a/src/core/operations/Register.mjs b/src/core/operations/Register.mjs index 3b0d7479..2f696edd 100644 --- a/src/core/operations/Register.mjs +++ b/src/core/operations/Register.mjs @@ -4,9 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Dish from "../Dish"; +import Operation from "../Operation.mjs"; +import Dish from "../Dish.mjs"; import XRegExp from "xregexp"; +import { isWorkerEnvironment } from "../Utils.mjs"; /** * Register operation @@ -72,7 +73,7 @@ class Register extends Operation { if (!registers) return state; - if (ENVIRONMENT_IS_WORKER()) { + if (isWorkerEnvironment()) { self.setRegisters(state.forkOffset + state.progress, state.numRegisters, registers.slice(1)); } @@ -101,7 +102,7 @@ class Register extends Operation { args = args.map(arg => { if (typeof arg !== "string" && typeof arg !== "object") return arg; - if (typeof arg === "object" && arg.hasOwnProperty("string")) { + if (typeof arg === "object" && Object.prototype.hasOwnProperty.call(arg, "string")) { arg.string = replaceRegister(arg.string); return arg; } diff --git a/src/core/operations/RegularExpression.mjs b/src/core/operations/RegularExpression.mjs index dd183def..5327870b 100644 --- a/src/core/operations/RegularExpression.mjs +++ b/src/core/operations/RegularExpression.mjs @@ -5,9 +5,9 @@ */ import XRegExp from "xregexp"; -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Regular expression operation diff --git a/src/core/operations/RemoveDiacritics.mjs b/src/core/operations/RemoveDiacritics.mjs index 217fafe1..dd814375 100644 --- a/src/core/operations/RemoveDiacritics.mjs +++ b/src/core/operations/RemoveDiacritics.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Remove Diacritics operation diff --git a/src/core/operations/RemoveEXIF.mjs b/src/core/operations/RemoveEXIF.mjs index 9c5cf49f..fff4f6b5 100644 --- a/src/core/operations/RemoveEXIF.mjs +++ b/src/core/operations/RemoveEXIF.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import { removeEXIF } from "../vendor/remove-exif"; -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import { removeEXIF } from "../vendor/remove-exif.mjs"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Remove EXIF operation @@ -27,17 +27,18 @@ class RemoveEXIF extends Operation { "EXIF data embedded in photos usually contains information about the image file itself as well as the device used to create it.", ].join("\n"); this.infoURL = "https://wikipedia.org/wiki/Exif"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "byteArray"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ run(input, args) { + input = new Uint8Array(input); // Do nothing if input is empty if (input.length === 0) return input; diff --git a/src/core/operations/RemoveLineNumbers.mjs b/src/core/operations/RemoveLineNumbers.mjs index d7c615f7..0fbf7115 100644 --- a/src/core/operations/RemoveLineNumbers.mjs +++ b/src/core/operations/RemoveLineNumbers.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Remove line numbers operation diff --git a/src/core/operations/RemoveNullBytes.mjs b/src/core/operations/RemoveNullBytes.mjs index dcbf9251..b633e82d 100644 --- a/src/core/operations/RemoveNullBytes.mjs +++ b/src/core/operations/RemoveNullBytes.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Remove null bytes operation @@ -20,17 +20,18 @@ class RemoveNullBytes extends Operation { this.name = "Remove null bytes"; this.module = "Default"; this.description = "Removes all null bytes (0x00) from the input."; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "byteArray"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ run(input, args) { + input = new Uint8Array(input); const output = []; for (let i = 0; i < input.length; i++) { if (input[i] !== 0) output.push(input[i]); diff --git a/src/core/operations/RemoveWhitespace.mjs b/src/core/operations/RemoveWhitespace.mjs index a6564809..a9ceda81 100644 --- a/src/core/operations/RemoveWhitespace.mjs +++ b/src/core/operations/RemoveWhitespace.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Remove whitespace operation diff --git a/src/core/operations/RenderImage.mjs b/src/core/operations/RenderImage.mjs index 07866eaf..2401a90b 100644 --- a/src/core/operations/RenderImage.mjs +++ b/src/core/operations/RenderImage.mjs @@ -4,12 +4,12 @@ * @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 {isImage} from "../lib/FileType"; +import { fromBase64, toBase64 } from "../lib/Base64.mjs"; +import { fromHex } from "../lib/Hex.mjs"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; +import {isImage} from "../lib/FileType.mjs"; /** * Render Image operation diff --git a/src/core/operations/ResizeImage.mjs b/src/core/operations/ResizeImage.mjs index 48a5d54a..57a8cd9f 100644 --- a/src/core/operations/ResizeImage.mjs +++ b/src/core/operations/ResizeImage.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; import { toBase64 } from "../lib/Base64.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; import jimp from "jimp"; /** @@ -25,8 +26,8 @@ class ResizeImage extends Operation { this.module = "Image"; this.description = "Resizes an image to the specified width and height values."; this.infoURL = "https://wikipedia.org/wiki/Image_scaling"; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -67,7 +68,7 @@ class ResizeImage extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ @@ -86,13 +87,13 @@ class ResizeImage extends Operation { "Bezier": jimp.RESIZE_BEZIER }; - if (!isImage(input)) { + if (!isImage(new Uint8Array(input))) { throw new OperationError("Invalid file type."); } let image; try { - image = await jimp.read(Buffer.from(input)); + image = await jimp.read(input); } catch (err) { throw new OperationError(`Error loading image. (${err})`); } @@ -102,7 +103,7 @@ class ResizeImage extends Operation { height = image.getHeight() * (height / 100); } - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Resizing image..."); if (aspect) { image.scaleToFit(width, height, resizeMap[resizeAlg]); @@ -110,8 +111,13 @@ class ResizeImage extends Operation { image.resize(width, height, resizeMap[resizeAlg]); } - const imageBuffer = await image.getBufferAsync(jimp.AUTO); - return [...imageBuffer]; + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; } catch (err) { throw new OperationError(`Error resizing image. (${err})`); } @@ -119,18 +125,19 @@ class ResizeImage extends Operation { /** * Displays the resized image using HTML for web apps - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/Return.mjs b/src/core/operations/Return.mjs index cc83bff8..2a1c6a38 100644 --- a/src/core/operations/Return.mjs +++ b/src/core/operations/Return.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Return operation diff --git a/src/core/operations/Reverse.mjs b/src/core/operations/Reverse.mjs index c88bb275..895d6723 100644 --- a/src/core/operations/Reverse.mjs +++ b/src/core/operations/Reverse.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Reverse operation diff --git a/src/core/operations/RotateImage.mjs b/src/core/operations/RotateImage.mjs index 34497863..b2075706 100644 --- a/src/core/operations/RotateImage.mjs +++ b/src/core/operations/RotateImage.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import { isImage } from "../lib/FileType"; -import { toBase64 } from "../lib/Base64"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; import jimp from "jimp"; /** @@ -25,8 +26,8 @@ class RotateImage extends Operation { this.module = "Image"; this.description = "Rotates an image by the specified number of degrees."; this.infoURL = ""; - this.inputType = "byteArray"; - this.outputType = "byteArray"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; this.presentType = "html"; this.args = [ { @@ -38,29 +39,35 @@ class RotateImage extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ async run(input, args) { const [degrees] = args; - if (!isImage(input)) { + if (!isImage(new Uint8Array(input))) { throw new OperationError("Invalid file type."); } let image; try { - image = await jimp.read(Buffer.from(input)); + image = await jimp.read(input); } catch (err) { throw new OperationError(`Error loading image. (${err})`); } try { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Rotating image..."); image.rotate(degrees); - const imageBuffer = await image.getBufferAsync(jimp.AUTO); - return [...imageBuffer]; + + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; } catch (err) { throw new OperationError(`Error rotating image. (${err})`); } @@ -68,18 +75,19 @@ class RotateImage extends Operation { /** * Displays the rotated image using HTML for web apps - * @param {byteArray} data + * @param {ArrayBuffer} data * @returns {html} */ present(data) { - if (!data.length) return ""; + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); - const type = isImage(data); + const type = isImage(dataArray); if (!type) { throw new OperationError("Invalid file type."); } - return ``; + return ``; } } diff --git a/src/core/operations/RotateLeft.mjs b/src/core/operations/RotateLeft.mjs index 4f73345d..e9ef8329 100644 --- a/src/core/operations/RotateLeft.mjs +++ b/src/core/operations/RotateLeft.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {rot, rotl, rotlCarry} from "../lib/Rotate"; +import Operation from "../Operation.mjs"; +import {rot, rotl, rotlCarry} from "../lib/Rotate.mjs"; /** diff --git a/src/core/operations/RotateRight.mjs b/src/core/operations/RotateRight.mjs index 455369b0..4e9451fc 100644 --- a/src/core/operations/RotateRight.mjs +++ b/src/core/operations/RotateRight.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {rot, rotr, rotrCarry} from "../lib/Rotate"; +import Operation from "../Operation.mjs"; +import {rot, rotr, rotrCarry} from "../lib/Rotate.mjs"; /** diff --git a/src/core/operations/SHA0.mjs b/src/core/operations/SHA0.mjs index b83a47d7..93345eb2 100644 --- a/src/core/operations/SHA0.mjs +++ b/src/core/operations/SHA0.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {runHash} from "../lib/Hash"; +import Operation from "../Operation.mjs"; +import {runHash} from "../lib/Hash.mjs"; /** * SHA0 operation diff --git a/src/core/operations/SHA1.mjs b/src/core/operations/SHA1.mjs index 2dc5ce5c..41a0105a 100644 --- a/src/core/operations/SHA1.mjs +++ b/src/core/operations/SHA1.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {runHash} from "../lib/Hash"; +import Operation from "../Operation.mjs"; +import {runHash} from "../lib/Hash.mjs"; /** * SHA1 operation diff --git a/src/core/operations/SHA2.mjs b/src/core/operations/SHA2.mjs index 05d4c253..c9599d24 100644 --- a/src/core/operations/SHA2.mjs +++ b/src/core/operations/SHA2.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {runHash} from "../lib/Hash"; +import Operation from "../Operation.mjs"; +import {runHash} from "../lib/Hash.mjs"; /** * SHA2 operation diff --git a/src/core/operations/SHA3.mjs b/src/core/operations/SHA3.mjs index cebb8e23..0f3cdef7 100644 --- a/src/core/operations/SHA3.mjs +++ b/src/core/operations/SHA3.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import JSSHA3 from "js-sha3"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * SHA3 operation diff --git a/src/core/operations/SQLBeautify.mjs b/src/core/operations/SQLBeautify.mjs index 1862972a..0f3d2e3c 100644 --- a/src/core/operations/SQLBeautify.mjs +++ b/src/core/operations/SQLBeautify.mjs @@ -5,7 +5,7 @@ */ import vkbeautify from "vkbeautify"; -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * SQL Beautify operation diff --git a/src/core/operations/SQLMinify.mjs b/src/core/operations/SQLMinify.mjs index d81e29ad..582ad43d 100644 --- a/src/core/operations/SQLMinify.mjs +++ b/src/core/operations/SQLMinify.mjs @@ -5,7 +5,7 @@ */ import vkbeautify from "vkbeautify"; -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * SQL Minify operation diff --git a/src/core/operations/SSDEEP.mjs b/src/core/operations/SSDEEP.mjs index 4ae5a650..87f5ee0b 100644 --- a/src/core/operations/SSDEEP.mjs +++ b/src/core/operations/SSDEEP.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import ssdeepjs from "ssdeep.js"; /** diff --git a/src/core/operations/SUB.mjs b/src/core/operations/SUB.mjs index f2374488..ef9777c6 100644 --- a/src/core/operations/SUB.mjs +++ b/src/core/operations/SUB.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import { bitOp, sub, BITWISE_OP_DELIMS } from "../lib/BitwiseOp"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import { bitOp, sub, BITWISE_OP_DELIMS } from "../lib/BitwiseOp.mjs"; /** * SUB operation diff --git a/src/core/operations/ScanForEmbeddedFiles.mjs b/src/core/operations/ScanForEmbeddedFiles.mjs index ae88134f..b1497407 100644 --- a/src/core/operations/ScanForEmbeddedFiles.mjs +++ b/src/core/operations/ScanForEmbeddedFiles.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {scanForFileTypes} from "../lib/FileType"; -import {FILE_SIGNATURES} from "../lib/FileSignatures"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import { scanForFileTypes } from "../lib/FileType.mjs"; +import { FILE_SIGNATURES } from "../lib/FileSignatures.mjs"; /** * Scan for Embedded Files operation @@ -55,12 +55,13 @@ class ScanForEmbeddedFiles extends Operation { if (types.length) { types.forEach(type => { numFound++; - output += "\nOffset " + type.offset + " (0x" + Utils.hex(type.offset) + "):\n" + - " File extension: " + type.fileDetails.extension + "\n" + - " MIME type: " + type.fileDetails.mime + "\n"; + output += `\nOffset ${type.offset} (0x${Utils.hex(type.offset)}): + File type: ${type.fileDetails.name} + Extension: ${type.fileDetails.extension} + MIME type: ${type.fileDetails.mime}\n`; if (type.fileDetails.description && type.fileDetails.description.length) { - output += " Description: " + type.fileDetails.description + "\n"; + output += ` Description: ${type.fileDetails.description}\n`; } }); } diff --git a/src/core/operations/ScatterChart.mjs b/src/core/operations/ScatterChart.mjs index 70aeb5ea..fe15f610 100644 --- a/src/core/operations/ScatterChart.mjs +++ b/src/core/operations/ScatterChart.mjs @@ -7,10 +7,10 @@ import * as d3temp from "d3"; import * as nodomtemp from "nodom"; -import { getScatterValues, getScatterValuesWithColour, RECORD_DELIMITER_OPTIONS, COLOURS, FIELD_DELIMITER_OPTIONS } from "../lib/Charts"; +import { getScatterValues, getScatterValuesWithColour, RECORD_DELIMITER_OPTIONS, COLOURS, FIELD_DELIMITER_OPTIONS } from "../lib/Charts.mjs"; -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; const d3 = d3temp.default ? d3temp.default : d3temp; const nodom = nodomtemp.default ? nodomtemp.default: nodomtemp; diff --git a/src/core/operations/Scrypt.mjs b/src/core/operations/Scrypt.mjs index 134a04fc..9c4ba304 100644 --- a/src/core/operations/Scrypt.mjs +++ b/src/core/operations/Scrypt.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; import scryptsy from "scryptsy"; +import { isWorkerEnvironment } from "../Utils.mjs"; /** * Scrypt operation @@ -73,7 +74,7 @@ class Scrypt extends Operation { input, salt, iterations, memFactor, parallelFactor, keyLength, p => { // Progress callback - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage(`Progress: ${p.percent.toFixed(0)}%`); } ); diff --git a/src/core/operations/SeriesChart.mjs b/src/core/operations/SeriesChart.mjs index cdae32b7..7a21953e 100644 --- a/src/core/operations/SeriesChart.mjs +++ b/src/core/operations/SeriesChart.mjs @@ -7,10 +7,10 @@ import * as d3temp from "d3"; import * as nodomtemp from "nodom"; -import { getSeriesValues, RECORD_DELIMITER_OPTIONS, FIELD_DELIMITER_OPTIONS } from "../lib/Charts"; +import { getSeriesValues, RECORD_DELIMITER_OPTIONS, FIELD_DELIMITER_OPTIONS } from "../lib/Charts.mjs"; -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; const d3 = d3temp.default ? d3temp.default : d3temp; const nodom = nodomtemp.default ? nodomtemp.default: nodomtemp; diff --git a/src/core/operations/SetDifference.mjs b/src/core/operations/SetDifference.mjs index ce272880..dc46c079 100644 --- a/src/core/operations/SetDifference.mjs +++ b/src/core/operations/SetDifference.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Set Difference operation diff --git a/src/core/operations/SetIntersection.mjs b/src/core/operations/SetIntersection.mjs index 87cb2b3c..7e6dbe10 100644 --- a/src/core/operations/SetIntersection.mjs +++ b/src/core/operations/SetIntersection.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Set Intersection operation diff --git a/src/core/operations/SetUnion.mjs b/src/core/operations/SetUnion.mjs index b835605e..80913063 100644 --- a/src/core/operations/SetUnion.mjs +++ b/src/core/operations/SetUnion.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Set Union operation diff --git a/src/core/operations/Shake.mjs b/src/core/operations/Shake.mjs index e096ac31..69e034e6 100644 --- a/src/core/operations/Shake.mjs +++ b/src/core/operations/Shake.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; import JSSHA3 from "js-sha3"; /** diff --git a/src/core/operations/SharpenImage.mjs b/src/core/operations/SharpenImage.mjs new file mode 100644 index 00000000..2ea8a122 --- /dev/null +++ b/src/core/operations/SharpenImage.mjs @@ -0,0 +1,169 @@ +/** + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isImage } from "../lib/FileType.mjs"; +import { toBase64 } from "../lib/Base64.mjs"; +import { gaussianBlur } from "../lib/ImageManipulation.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; +import jimp from "jimp"; + +/** + * Sharpen Image operation + */ +class SharpenImage extends Operation { + + /** + * SharpenImage constructor + */ + constructor() { + super(); + + this.name = "Sharpen Image"; + this.module = "Image"; + this.description = "Sharpens an image (Unsharp mask)"; + this.infoURL = "https://wikipedia.org/wiki/Unsharp_masking"; + this.inputType = "ArrayBuffer"; + this.outputType = "ArrayBuffer"; + this.presentType = "html"; + this.args = [ + { + name: "Radius", + type: "number", + value: 2, + min: 1 + }, + { + name: "Amount", + type: "number", + value: 1, + min: 0, + step: 0.1 + }, + { + name: "Threshold", + type: "number", + value: 10, + min: 0, + max: 100 + } + ]; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {byteArray} + */ + async run(input, args) { + const [radius, amount, threshold] = args; + + if (!isImage(new Uint8Array(input))){ + throw new OperationError("Invalid file type."); + } + + let image; + try { + image = await jimp.read(input); + } catch (err) { + throw new OperationError(`Error loading image. (${err})`); + } + + try { + if (isWorkerEnvironment()) + self.sendStatusMessage("Sharpening image... (Cloning image)"); + const blurMask = image.clone(); + + if (isWorkerEnvironment()) + self.sendStatusMessage("Sharpening image... (Blurring cloned image)"); + const blurImage = gaussianBlur(image.clone(), radius, 3); + + + if (isWorkerEnvironment()) + self.sendStatusMessage("Sharpening image... (Creating unsharp mask)"); + blurMask.scan(0, 0, blurMask.bitmap.width, blurMask.bitmap.height, function(x, y, idx) { + const blurRed = blurImage.bitmap.data[idx]; + const blurGreen = blurImage.bitmap.data[idx + 1]; + const blurBlue = blurImage.bitmap.data[idx + 2]; + + const normalRed = this.bitmap.data[idx]; + const normalGreen = this.bitmap.data[idx + 1]; + const normalBlue = this.bitmap.data[idx + 2]; + + // Subtract blurred pixel value from normal image + this.bitmap.data[idx] = (normalRed > blurRed) ? normalRed - blurRed : 0; + this.bitmap.data[idx + 1] = (normalGreen > blurGreen) ? normalGreen - blurGreen : 0; + this.bitmap.data[idx + 2] = (normalBlue > blurBlue) ? normalBlue - blurBlue : 0; + }); + + if (isWorkerEnvironment()) + self.sendStatusMessage("Sharpening image... (Merging with unsharp mask)"); + image.scan(0, 0, image.bitmap.width, image.bitmap.height, function(x, y, idx) { + let maskRed = blurMask.bitmap.data[idx]; + let maskGreen = blurMask.bitmap.data[idx + 1]; + let maskBlue = blurMask.bitmap.data[idx + 2]; + + const normalRed = this.bitmap.data[idx]; + const normalGreen = this.bitmap.data[idx + 1]; + const normalBlue = this.bitmap.data[idx + 2]; + + // Calculate luminance + const maskLuminance = (0.2126 * maskRed + 0.7152 * maskGreen + 0.0722 * maskBlue); + const normalLuminance = (0.2126 * normalRed + 0.7152 * normalGreen + 0.0722 * normalBlue); + + let luminanceDiff; + if (maskLuminance > normalLuminance) { + luminanceDiff = maskLuminance - normalLuminance; + } else { + luminanceDiff = normalLuminance - maskLuminance; + } + + // Scale mask colours by amount + maskRed = maskRed * amount; + maskGreen = maskGreen * amount; + maskBlue = maskBlue * amount; + + // Only change pixel value if the difference is higher than threshold + if ((luminanceDiff / 255) * 100 >= threshold) { + this.bitmap.data[idx] = (normalRed + maskRed) <= 255 ? normalRed + maskRed : 255; + this.bitmap.data[idx + 1] = (normalGreen + maskGreen) <= 255 ? normalGreen + maskGreen : 255; + this.bitmap.data[idx + 2] = (normalBlue + maskBlue) <= 255 ? normalBlue + maskBlue : 255; + } + }); + + let imageBuffer; + if (image.getMIME() === "image/gif") { + imageBuffer = await image.getBufferAsync(jimp.MIME_PNG); + } else { + imageBuffer = await image.getBufferAsync(jimp.AUTO); + } + return imageBuffer.buffer; + } catch (err) { + throw new OperationError(`Error sharpening image. (${err})`); + } + } + + /** + * Displays the sharpened image using HTML for web apps + * @param {ArrayBuffer} data + * @returns {html} + */ + present(data) { + if (!data.byteLength) return ""; + const dataArray = new Uint8Array(data); + + const type = isImage(dataArray); + if (!type) { + throw new OperationError("Invalid file type."); + } + + return ``; + } + +} + +export default SharpenImage; diff --git a/src/core/operations/ShowBase64Offsets.mjs b/src/core/operations/ShowBase64Offsets.mjs index d028400d..37d8a6ce 100644 --- a/src/core/operations/ShowBase64Offsets.mjs +++ b/src/core/operations/ShowBase64Offsets.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {fromBase64, toBase64} from "../lib/Base64"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {fromBase64, toBase64} from "../lib/Base64.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Show Base64 offsets operation diff --git a/src/core/operations/Sleep.mjs b/src/core/operations/Sleep.mjs index 4cd71bfe..1e7b657a 100644 --- a/src/core/operations/Sleep.mjs +++ b/src/core/operations/Sleep.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Sleep operation diff --git a/src/core/operations/Snefru.mjs b/src/core/operations/Snefru.mjs index b7cd65b5..520b3550 100644 --- a/src/core/operations/Snefru.mjs +++ b/src/core/operations/Snefru.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {runHash} from "../lib/Hash"; +import Operation from "../Operation.mjs"; +import {runHash} from "../lib/Hash.mjs"; /** * Snefru operation diff --git a/src/core/operations/Sort.mjs b/src/core/operations/Sort.mjs index 7e024d45..84bce4c9 100644 --- a/src/core/operations/Sort.mjs +++ b/src/core/operations/Sort.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {INPUT_DELIM_OPTIONS} from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {INPUT_DELIM_OPTIONS} from "../lib/Delim.mjs"; /** * Sort operation diff --git a/src/core/operations/Split.mjs b/src/core/operations/Split.mjs index 1340a383..0da1f101 100644 --- a/src/core/operations/Split.mjs +++ b/src/core/operations/Split.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {SPLIT_DELIM_OPTIONS, JOIN_DELIM_OPTIONS} from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import {SPLIT_DELIM_OPTIONS, JOIN_DELIM_OPTIONS} from "../lib/Delim.mjs"; /** * Split operation diff --git a/src/core/operations/SplitColourChannels.mjs b/src/core/operations/SplitColourChannels.mjs index c38af409..e8d23ff0 100644 --- a/src/core/operations/SplitColourChannels.mjs +++ b/src/core/operations/SplitColourChannels.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import Utils from "../Utils"; -import {isImage} from "../lib/FileType"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; +import {isImage} from "../lib/FileType.mjs"; import jimp from "jimp"; @@ -26,18 +26,19 @@ class SplitColourChannels extends Operation { this.module = "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.inputType = "ArrayBuffer"; this.outputType = "List"; this.presentType = "html"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {List} */ async run(input, args) { + input = new Uint8Array(input); // Make sure that the input is an image if (!isImage(input)) throw new OperationError("Invalid file type."); diff --git a/src/core/operations/StandardDeviation.mjs b/src/core/operations/StandardDeviation.mjs index d02bd93c..d0509dcd 100644 --- a/src/core/operations/StandardDeviation.mjs +++ b/src/core/operations/StandardDeviation.mjs @@ -6,9 +6,9 @@ */ import BigNumber from "bignumber.js"; -import Operation from "../Operation"; -import { stdDev, createNumArray } from "../lib/Arithmetic"; -import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import { stdDev, createNumArray } from "../lib/Arithmetic.mjs"; +import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim.mjs"; /** diff --git a/src/core/operations/Streebog.mjs b/src/core/operations/Streebog.mjs new file mode 100644 index 00000000..c5e5bb89 --- /dev/null +++ b/src/core/operations/Streebog.mjs @@ -0,0 +1,60 @@ +/** + * @author mshwed [m@ttshwed.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import GostDigest from "../vendor/gost/gostDigest.mjs"; +import {toHexFast} from "../lib/Hex.mjs"; + +/** + * Streebog operation + */ +class Streebog extends Operation { + + /** + * Streebog constructor + */ + constructor() { + super(); + + this.name = "Streebog"; + this.module = "Hashing"; + this.description = "Streebog is a cryptographic hash function defined in the Russian national standard GOST R 34.11-2012 Information Technology \u2013 Cryptographic Information Security \u2013 Hash Function. It was created to replace an obsolete GOST hash function defined in the old standard GOST R 34.11-94, and as an asymmetric reply to SHA-3 competition by the US National Institute of Standards and Technology."; + this.infoURL = "https://wikipedia.org/wiki/Streebog"; + this.inputType = "ArrayBuffer"; + this.outputType = "string"; + this.args = [ + { + "name": "Size", + "type": "option", + "value": ["256", "512"] + } + ]; + } + + /** + * @param {ArrayBuffer} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + try { + const length = parseInt(args[0], 10); + const gostDigest = new GostDigest({ + name: "GOST R 34.11", + version: 2012, + length: length + }); + + return toHexFast(gostDigest.digest(input)); + } catch (err) { + throw new OperationError(err); + } + } + +} + +export default Streebog; diff --git a/src/core/operations/Strings.mjs b/src/core/operations/Strings.mjs index 11ffd3af..ddf1d49d 100644 --- a/src/core/operations/Strings.mjs +++ b/src/core/operations/Strings.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import XRegExp from "xregexp"; -import { search } from "../lib/Extract"; +import { search } from "../lib/Extract.mjs"; /** * Strings operation diff --git a/src/core/operations/StripHTMLTags.mjs b/src/core/operations/StripHTMLTags.mjs index f1b7b08e..6935c1c0 100644 --- a/src/core/operations/StripHTMLTags.mjs +++ b/src/core/operations/StripHTMLTags.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * Strip HTML tags operation diff --git a/src/core/operations/StripHTTPHeaders.mjs b/src/core/operations/StripHTTPHeaders.mjs index 0473c2dd..e43360ed 100644 --- a/src/core/operations/StripHTTPHeaders.mjs +++ b/src/core/operations/StripHTTPHeaders.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Strip HTTP headers operation diff --git a/src/core/operations/Subsection.mjs b/src/core/operations/Subsection.mjs index 548780c8..86980e40 100644 --- a/src/core/operations/Subsection.mjs +++ b/src/core/operations/Subsection.mjs @@ -4,10 +4,9 @@ * @license Apache-2.0 */ -import XRegExp from "xregexp"; -import Operation from "../Operation"; -import Recipe from "../Recipe"; -import Dish from "../Dish"; +import Operation from "../Operation.mjs"; +import Recipe from "../Recipe.mjs"; +import Dish from "../Dish.mjs"; /** * Subsection operation @@ -22,7 +21,7 @@ class Subsection extends Operation { this.name = "Subsection"; this.flowControl = true; - this.module = "Regex"; + this.module = "Default"; 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"; @@ -87,7 +86,7 @@ class Subsection extends Operation { if (!caseSensitive) flags += "i"; if (global) flags += "g"; - const regex = new XRegExp(section, flags), + const regex = new RegExp(section, flags), recipe = new Recipe(); recipe.addOperations(subOpList); diff --git a/src/core/operations/Substitute.mjs b/src/core/operations/Substitute.mjs index 6b3a2e8b..39f13e89 100644 --- a/src/core/operations/Substitute.mjs +++ b/src/core/operations/Substitute.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * Substitute operation diff --git a/src/core/operations/Subtract.mjs b/src/core/operations/Subtract.mjs index 10de142b..3d9cb176 100644 --- a/src/core/operations/Subtract.mjs +++ b/src/core/operations/Subtract.mjs @@ -6,9 +6,9 @@ */ import BigNumber from "bignumber.js"; -import Operation from "../Operation"; -import { sub, createNumArray } from "../lib/Arithmetic"; -import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import { sub, createNumArray } from "../lib/Arithmetic.mjs"; +import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim.mjs"; /** diff --git a/src/core/operations/Sum.mjs b/src/core/operations/Sum.mjs index 18504106..dd7ebd3b 100644 --- a/src/core/operations/Sum.mjs +++ b/src/core/operations/Sum.mjs @@ -6,9 +6,9 @@ */ import BigNumber from "bignumber.js"; -import Operation from "../Operation"; -import { sum, createNumArray } from "../lib/Arithmetic"; -import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import { sum, createNumArray } from "../lib/Arithmetic.mjs"; +import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim.mjs"; /** diff --git a/src/core/operations/SwapEndianness.mjs b/src/core/operations/SwapEndianness.mjs index 02958414..d0a7492b 100644 --- a/src/core/operations/SwapEndianness.mjs +++ b/src/core/operations/SwapEndianness.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {toHex, fromHex} from "../lib/Hex"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {toHex, fromHex} from "../lib/Hex.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Swap endianness operation diff --git a/src/core/operations/SymmetricDifference.mjs b/src/core/operations/SymmetricDifference.mjs index 249f8086..f9044769 100644 --- a/src/core/operations/SymmetricDifference.mjs +++ b/src/core/operations/SymmetricDifference.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Utils from "../Utils"; -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Utils from "../Utils.mjs"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Set Symmetric Difference operation diff --git a/src/core/operations/SyntaxHighlighter.mjs b/src/core/operations/SyntaxHighlighter.mjs index a3f2d273..b8c578d6 100644 --- a/src/core/operations/SyntaxHighlighter.mjs +++ b/src/core/operations/SyntaxHighlighter.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import hljs from "highlight.js"; /** diff --git a/src/core/operations/TCPIPChecksum.mjs b/src/core/operations/TCPIPChecksum.mjs index 1da73359..0e5c6c60 100644 --- a/src/core/operations/TCPIPChecksum.mjs +++ b/src/core/operations/TCPIPChecksum.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * TCP/IP Checksum operation @@ -22,17 +22,18 @@ class TCPIPChecksum extends Operation { 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"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { + input = new Uint8Array(input); let csum = 0; for (let i = 0; i < input.length; i++) { diff --git a/src/core/operations/Tail.mjs b/src/core/operations/Tail.mjs index adbbbecc..8a29bb2d 100644 --- a/src/core/operations/Tail.mjs +++ b/src/core/operations/Tail.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {INPUT_DELIM_OPTIONS} from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {INPUT_DELIM_OPTIONS} from "../lib/Delim.mjs"; /** * Tail operation diff --git a/src/core/operations/TakeBytes.mjs b/src/core/operations/TakeBytes.mjs index 4bf6d951..431bbee1 100644 --- a/src/core/operations/TakeBytes.mjs +++ b/src/core/operations/TakeBytes.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * Take bytes operation diff --git a/src/core/operations/Tar.mjs b/src/core/operations/Tar.mjs index 84674bff..3078d959 100644 --- a/src/core/operations/Tar.mjs +++ b/src/core/operations/Tar.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * Tar operation @@ -22,7 +22,7 @@ class Tar extends Operation { this.module = "Compression"; this.description = "Packs the input into a tarball.

No support for multiple files at this time."; this.infoURL = "https://wikipedia.org/wiki/Tar_(computing)"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "File"; this.args = [ { @@ -34,11 +34,13 @@ class Tar extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ run(input, args) { + input = new Uint8Array(input); + const Tarball = function() { this.bytes = new Array(512); this.position = 0; diff --git a/src/core/operations/TextEncodingBruteForce.mjs b/src/core/operations/TextEncodingBruteForce.mjs index 169e6ad7..e7e56b66 100644 --- a/src/core/operations/TextEncodingBruteForce.mjs +++ b/src/core/operations/TextEncodingBruteForce.mjs @@ -5,10 +5,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; import cptable from "../vendor/js-codepage/cptable.js"; -import {IO_FORMAT} from "../lib/ChrEnc"; +import {IO_FORMAT} from "../lib/ChrEnc.mjs"; /** * Text Encoding Brute Force operation diff --git a/src/core/operations/ToBCD.mjs b/src/core/operations/ToBCD.mjs index c8fd0c89..3908742c 100644 --- a/src/core/operations/ToBCD.mjs +++ b/src/core/operations/ToBCD.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; -import {ENCODING_SCHEME, ENCODING_LOOKUP, FORMAT} from "../lib/BCD"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {ENCODING_SCHEME, ENCODING_LOOKUP, FORMAT} from "../lib/BCD.mjs"; import BigNumber from "bignumber.js"; /** diff --git a/src/core/operations/ToBase.mjs b/src/core/operations/ToBase.mjs index 93107903..09a91571 100644 --- a/src/core/operations/ToBase.mjs +++ b/src/core/operations/ToBase.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * To Base operation diff --git a/src/core/operations/ToBase32.mjs b/src/core/operations/ToBase32.mjs index fd0a6136..fd36f550 100644 --- a/src/core/operations/ToBase32.mjs +++ b/src/core/operations/ToBase32.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * To Base32 operation @@ -22,7 +22,7 @@ class ToBase32 extends Operation { this.module = "Default"; this.description = "Base32 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. It uses a smaller set of characters than Base64, usually the uppercase alphabet and the numbers 2 to 7."; this.infoURL = "https://wikipedia.org/wiki/Base32"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { @@ -34,19 +34,19 @@ class ToBase32 extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { if (!input) return ""; + input = new Uint8Array(input); const alphabet = args[0] ? Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="; let output = "", chr1, chr2, chr3, chr4, chr5, enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8, i = 0; - while (i < input.length) { chr1 = input[i++]; chr2 = input[i++]; @@ -77,7 +77,6 @@ class ToBase32 extends Operation { alphabet.charAt(enc4) + alphabet.charAt(enc5) + alphabet.charAt(enc6) + alphabet.charAt(enc7) + alphabet.charAt(enc8); } - return output; } diff --git a/src/core/operations/ToBase58.mjs b/src/core/operations/ToBase58.mjs index 3e2c6a60..5353c40e 100644 --- a/src/core/operations/ToBase58.mjs +++ b/src/core/operations/ToBase58.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; -import {ALPHABET_OPTIONS} from "../lib/Base58"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {ALPHABET_OPTIONS} from "../lib/Base58.mjs"; /** * To Base58 operation @@ -24,7 +24,7 @@ class ToBase58 extends Operation { this.module = "Default"; this.description = "Base58 (similar to Base64) is a notation for encoding arbitrary byte data. It differs from Base64 by removing easily misread characters (i.e. l, I, 0 and O) to improve human readability.

This operation encodes data in an ASCII string (with an alphabet of your choosing, presets included).

e.g. hello world becomes StV1DL6CwTryKyV

Base58 is commonly used in cryptocurrencies (Bitcoin, Ripple, etc)."; this.infoURL = "https://wikipedia.org/wiki/Base58"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { @@ -36,11 +36,12 @@ class ToBase58 extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { + input = new Uint8Array(input); let alphabet = args[0] || ALPHABET_OPTIONS[0].value, result = [0]; diff --git a/src/core/operations/ToBase62.mjs b/src/core/operations/ToBase62.mjs index 51f89ecd..c5d2f35e 100644 --- a/src/core/operations/ToBase62.mjs +++ b/src/core/operations/ToBase62.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import BigNumber from "bignumber.js"; -import Utils from "../Utils"; -import {toHexFast} from "../lib/Hex"; +import Utils from "../Utils.mjs"; +import {toHexFast} from "../lib/Hex.mjs"; /** * To Base62 operation @@ -24,7 +24,7 @@ class ToBase62 extends Operation { this.module = "Default"; this.description = "Base62 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. The high number base results in shorter strings than with the decimal or hexadecimal system."; this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { @@ -36,11 +36,12 @@ class ToBase62 extends Operation { } /** - * @param {string} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { + input = new Uint8Array(input); if (input.length < 1) return ""; const ALPHABET = Utils.expandAlphRange(args[0]).join(""); diff --git a/src/core/operations/ToBase64.mjs b/src/core/operations/ToBase64.mjs index cffc3140..93dd4f81 100644 --- a/src/core/operations/ToBase64.mjs +++ b/src/core/operations/ToBase64.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {toBase64, ALPHABET_OPTIONS} from "../lib/Base64"; +import Operation from "../Operation.mjs"; +import {toBase64, ALPHABET_OPTIONS} from "../lib/Base64.mjs"; /** * To Base64 operation diff --git a/src/core/operations/ToBase85.mjs b/src/core/operations/ToBase85.mjs index 97cc2e72..839ef1e4 100644 --- a/src/core/operations/ToBase85.mjs +++ b/src/core/operations/ToBase85.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import Utils from "../Utils"; -import {alphabetName, ALPHABET_OPTIONS} from "../lib/Base85"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; +import {alphabetName, ALPHABET_OPTIONS} from "../lib/Base85.mjs"; /** * To Base85 operation @@ -24,7 +24,7 @@ class ToBase85 extends Operation { this.module = "Default"; this.description = "Base85 (also called Ascii85) is a notation for encoding arbitrary byte data. It is usually more efficient that Base64.

This operation encodes data in an ASCII string (with an alphabet of your choosing, presets included).

e.g. hello world becomes BOu!rD]j7BEbo7

Base85 is commonly used in Adobe's PostScript and PDF file formats.

Options
Alphabet
  • Standard - The standard alphabet, referred to as Ascii85
  • Z85 (ZeroMQ) - A string-safe variant of Base85, which avoids quote marks and backslash characters
  • IPv6 - A variant of Base85 suitable for encoding IPv6 addresses (RFC 1924)
Include delimiter
Adds a '<~' and '~>' delimiter to the start and end of the data. This is standard for Adobe's implementation of Base85."; this.infoURL = "https://wikipedia.org/wiki/Ascii85"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { @@ -41,11 +41,12 @@ class ToBase85 extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { + input = new Uint8Array(input); const alphabet = Utils.expandAlphRange(args[0]).join(""), encoding = alphabetName(alphabet), includeDelim = args[1]; diff --git a/src/core/operations/ToBinary.mjs b/src/core/operations/ToBinary.mjs index af9087d1..95d004b0 100644 --- a/src/core/operations/ToBinary.mjs +++ b/src/core/operations/ToBinary.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {BIN_DELIM_OPTIONS} from "../lib/Delim"; -import {toBinary} from "../lib/Binary"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {BIN_DELIM_OPTIONS} from "../lib/Delim.mjs"; +import {toBinary} from "../lib/Binary.mjs"; /** * To Binary operation @@ -24,7 +24,7 @@ class ToBinary extends Operation { this.module = "Default"; this.description = "Displays the input data as a binary string.

e.g. Hi becomes 01001000 01101001"; this.infoURL = "https://wikipedia.org/wiki/Binary_code"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { @@ -36,11 +36,12 @@ class ToBinary extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { + input = new Uint8Array(input); return toBinary(input, args[0]); } diff --git a/src/core/operations/ToBraille.mjs b/src/core/operations/ToBraille.mjs index 9530f9d3..fcf13de2 100644 --- a/src/core/operations/ToBraille.mjs +++ b/src/core/operations/ToBraille.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {BRAILLE_LOOKUP} from "../lib/Braille"; +import Operation from "../Operation.mjs"; +import {BRAILLE_LOOKUP} from "../lib/Braille.mjs"; /** * To Braille operation diff --git a/src/core/operations/ToCamelCase.mjs b/src/core/operations/ToCamelCase.mjs index 3553810e..8d7c5445 100644 --- a/src/core/operations/ToCamelCase.mjs +++ b/src/core/operations/ToCamelCase.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import camelCase from "lodash/camelCase"; -import Operation from "../Operation"; -import { replaceVariableNames } from "../lib/Code"; +import camelCase from "lodash/camelCase.js"; +import Operation from "../Operation.mjs"; +import { replaceVariableNames } from "../lib/Code.mjs"; /** * To Camel case operation diff --git a/src/core/operations/ToCaseInsensitiveRegex.mjs b/src/core/operations/ToCaseInsensitiveRegex.mjs index 0d606a05..28bd3dc9 100644 --- a/src/core/operations/ToCaseInsensitiveRegex.mjs +++ b/src/core/operations/ToCaseInsensitiveRegex.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * To Case Insensitive Regex operation diff --git a/src/core/operations/ToCharcode.mjs b/src/core/operations/ToCharcode.mjs index 11006685..5ad287e4 100644 --- a/src/core/operations/ToCharcode.mjs +++ b/src/core/operations/ToCharcode.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {DELIM_OPTIONS} from "../lib/Delim"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import { DELIM_OPTIONS } from "../lib/Delim.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; /** * To Charcode operation @@ -69,11 +70,11 @@ class ToCharcode extends Operation { else if (ordinal < 4294967296) padding = 8; else padding = 2; - if (padding > 2 && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false); + if (padding > 2 && isWorkerEnvironment()) self.setOption("attemptHighlight", false); output += Utils.hex(ordinal, padding) + delim; } else { - if (ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false); + if (isWorkerEnvironment()) self.setOption("attemptHighlight", false); output += ordinal.toString(base) + delim; } } diff --git a/src/core/operations/ToDecimal.mjs b/src/core/operations/ToDecimal.mjs index 6df51ae2..65798a7c 100644 --- a/src/core/operations/ToDecimal.mjs +++ b/src/core/operations/ToDecimal.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {DELIM_OPTIONS} from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {DELIM_OPTIONS} from "../lib/Delim.mjs"; /** @@ -23,7 +23,7 @@ class ToDecimal extends Operation { this.name = "To Decimal"; this.module = "Default"; this.description = "Converts the input data to an ordinal integer array.

e.g. Hello becomes 72 101 108 108 111"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { @@ -40,11 +40,12 @@ class ToDecimal extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { + input = new Uint8Array(input); const delim = Utils.charRep(args[0]), signed = args[1]; if (signed) { diff --git a/src/core/operations/ToHTMLEntity.mjs b/src/core/operations/ToHTMLEntity.mjs index 53ec4e34..f2a57a43 100644 --- a/src/core/operations/ToHTMLEntity.mjs +++ b/src/core/operations/ToHTMLEntity.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * To HTML Entity operation @@ -19,7 +19,7 @@ class ToHTMLEntity extends Operation { super(); this.name = "To HTML Entity"; - this.module = "Default"; + this.module = "Encodings"; this.description = "Converts characters to HTML entities

e.g. & becomes &amp;"; this.infoURL = "https://wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references"; this.inputType = "string"; @@ -33,7 +33,7 @@ class ToHTMLEntity extends Operation { { "name": "Convert to", "type": "option", - "value": ["Named entities where possible", "Numeric entities", "Hex entities"] + "value": ["Named entities", "Numeric entities", "Hex entities"] } ]; } @@ -59,13 +59,13 @@ class ToHTMLEntity extends Operation { } else if (convertAll) { output += byteToEntity[charcodes[i]] || "&#" + charcodes[i] + ";"; } else if (numeric) { - if (charcodes[i] > 255 || byteToEntity.hasOwnProperty(charcodes[i])) { + if (charcodes[i] > 255 || charcodes[i] in byteToEntity) { output += "&#" + charcodes[i] + ";"; } else { output += Utils.chr(charcodes[i]); } } else if (hexa) { - if (charcodes[i] > 255 || byteToEntity.hasOwnProperty(charcodes[i])) { + if (charcodes[i] > 255 || charcodes[i] in byteToEntity) { output += "&#x" + Utils.hex(charcodes[i]) + ";"; } else { output += Utils.chr(charcodes[i]); @@ -87,11 +87,38 @@ class ToHTMLEntity extends Operation { * Lookup table to translate byte values to their HTML entity codes. */ const byteToEntity = { + 9: " ", + 10: " ", + 33: "!", 34: """, + 35: "#", + 36: "$", + 37: "%", 38: "&", 39: "'", + 40: "(", + 41: ")", + 42: "*", + 43: "+", + 44: ",", + 46: ".", + 47: "/", + 58: ":", + 59: ";", 60: "<", + 61: "=", 62: ">", + 63: "?", + 64: "@", + 91: "[", + 92: "\", + 93: "]", + 94: "^", + 95: "_", + 96: "`", + 123: "{", + 124: "|", + 125: "}", 160: " ", 161: "¡", 162: "¢", @@ -188,14 +215,139 @@ const byteToEntity = { 253: "ý", 254: "þ", 255: "ÿ", + 256: "Ā", + 257: "ā", + 258: "Ă", + 259: "ă", + 260: "Ą", + 261: "ą", + 262: "Ć", + 263: "ć", + 264: "Ĉ", + 265: "ĉ", + 266: "Ċ", + 267: "ċ", + 268: "Č", + 269: "č", + 270: "Ď", + 271: "ď", + 272: "Đ", + 273: "đ", + 274: "Ē", + 275: "ē", + 278: "Ė", + 279: "ė", + 280: "Ę", + 281: "ę", + 282: "Ě", + 283: "ě", + 284: "Ĝ", + 285: "ĝ", + 286: "Ğ", + 287: "ğ", + 288: "Ġ", + 289: "ġ", + 290: "Ģ", + 292: "Ĥ", + 293: "ĥ", + 294: "Ħ", + 295: "ħ", + 296: "Ĩ", + 297: "ĩ", + 298: "Ī", + 299: "ī", + 302: "Į", + 303: "į", + 304: "İ", + 305: "ı", + 306: "IJ", + 307: "ij", + 308: "Ĵ", + 309: "ĵ", + 310: "Ķ", + 311: "ķ", + 312: "ĸ", + 313: "Ĺ", + 314: "ĺ", + 315: "Ļ", + 316: "ļ", + 317: "Ľ", + 318: "ľ", + 319: "Ŀ", + 320: "ŀ", + 321: "Ł", + 322: "ł", + 323: "Ń", + 324: "ń", + 325: "Ņ", + 326: "ņ", + 327: "Ň", + 328: "ň", + 329: "ʼn", + 330: "Ŋ", + 331: "ŋ", + 332: "Ō", + 333: "ō", + 336: "Ő", + 337: "ő", 338: "Œ", 339: "œ", + 340: "Ŕ", + 341: "ŕ", + 342: "Ŗ", + 343: "ŗ", + 344: "Ř", + 345: "ř", + 346: "Ś", + 347: "ś", + 348: "Ŝ", + 349: "ŝ", + 350: "Ş", + 351: "ş", 352: "Š", 353: "š", + 354: "Ţ", + 355: "ţ", + 356: "Ť", + 357: "ť", + 358: "Ŧ", + 359: "ŧ", + 360: "Ũ", + 361: "ũ", + 362: "Ū", + 363: "ū", + 364: "Ŭ", + 365: "ŭ", + 366: "Ů", + 367: "ů", + 368: "Ű", + 369: "ű", + 370: "Ų", + 371: "ų", + 372: "Ŵ", + 373: "ŵ", + 374: "Ŷ", + 375: "ŷ", 376: "Ÿ", + 377: "Ź", + 378: "ź", + 379: "Ż", + 380: "ż", + 381: "Ž", + 382: "ž", 402: "ƒ", + 437: "Ƶ", + 501: "ǵ", + 567: "ȷ", 710: "ˆ", + 711: "ˇ", + 728: "˘", + 729: "˙", + 730: "˚", + 731: "˛", 732: "˜", + 785: "̑", + 818: "_", 913: "Α", 914: "Β", 915: "Γ", @@ -247,16 +399,124 @@ const byteToEntity = { 969: "ω", 977: "ϑ", 978: "ϒ", + 981: "ϕ", 982: "ϖ", + 988: "Ϝ", + 989: "ϝ", + 1008: "ϰ", + 1009: "ϱ", + 1013: "ε,", + 1014: "϶", + 1025: "Ё", + 1026: "Ђ", + 1027: "Ѓ", + 1028: "Є", + 1029: "Ѕ", + 1030: "І", + 1031: "Ї", + 1032: "Ј", + 1033: "Љ", + 1034: "Њ", + 1035: "Ћ", + 1036: "Ќ", + 1038: "Ў", + 1039: "Џ", + 1040: "А", + 1041: "Б", + 1042: "В", + 1043: "Г", + 1044: "Д", + 1045: "Е", + 1046: "Ж", + 1047: "З", + 1048: "И", + 1049: "Й", + 1050: "К", + 1051: "Л", + 1052: "М", + 1053: "Н", + 1054: "О", + 1055: "П", + 1056: "Р", + 1057: "С", + 1058: "Т", + 1059: "У", + 1060: "Ф", + 1061: "Х", + 1062: "Ц", + 1063: "Ч", + 1064: "Ш", + 1065: "Щ", + 1066: "Ъ", + 1067: "Ы", + 1068: "Ь", + 1069: "Э", + 1070: "Ю", + 1071: "Я", + 1072: "а", + 1073: "б", + 1074: "в", + 1075: "г", + 1076: "д", + 1077: "е", + 1078: "ж", + 1079: "з", + 1080: "и", + 1081: "й", + 1082: "к", + 1083: "л", + 1084: "м", + 1085: "н", + 1086: "о", + 1087: "п", + 1088: "р", + 1089: "с", + 1090: "т", + 1091: "у", + 1092: "ф", + 1093: "х", + 1094: "ц", + 1095: "ч", + 1096: "ш", + 1097: "щ", + 1098: "ъ", + 1099: "ы", + 1100: "ь", + 1101: "э", + 1102: "ю", + 1103: "я", + 1105: "ё", + 1106: "ђ", + 1107: "ѓ", + 1108: "є", + 1109: "ѕ", + 1110: "і", + 1111: "ї", + 1112: "ј", + 1113: "љ", + 1114: "њ", + 1115: "ћ", + 1116: "ќ", + 1118: "ў", + 1119: "џ", 8194: " ", 8195: " ", + 8196: " ", + 8197: " ", + 8199: " ", + 8200: " ", 8201: " ", + 8202: " ", + 8203: "​", 8204: "‌", 8205: "‍", 8206: "‎", 8207: "‏", + 8208: "‐", 8211: "–", 8212: "—", + 8213: "―", + 8214: "‖", 8216: "‘", 8217: "’", 8218: "‚", @@ -266,81 +526,987 @@ const byteToEntity = { 8224: "†", 8225: "‡", 8226: "•", + 8229: "‥", 8230: "…", 8240: "‰", + 8241: "‱", 8242: "′", 8243: "″", + 8244: "‴", + 8245: "‵", 8249: "‹", 8250: "›", 8254: "‾", + 8257: "⁁", + 8259: "⁃", 8260: "⁄", + 8271: "⁏", + 8279: "⁗", + 8287: " ", + 8288: "⁠", + 8289: "⁡", + 8290: "⁢", + 8291: "⁣", 8364: "€", + 8411: "⃛", + 8412: "⃜", + 8450: "ℂ", + 8453: "℅", + 8458: "ℊ", + 8459: "ℋ", + 8460: "ℌ", + 8461: "ℍ", + 8462: "ℎ", + 8463: "ℏ", + 8464: "ℐ", 8465: "ℑ", + 8466: "ℒ", + 8467: "ℓ", + 8469: "ℕ", + 8470: "№", + 8471: "℗", 8472: "℘", + 8473: "ℙ", + 8474: "ℚ", + 8475: "ℛ", 8476: "ℜ", + 8477: "ℝ", + 8478: "℞", 8482: "™", + 8484: "ℤ", + 8486: "Ω", + 8487: "℧", + 8488: "ℨ", + 8489: "℩", + 8491: "Å", + 8492: "ℬ", + 8493: "ℭ", + 8495: "ℯ", + 8496: "ℰ", + 8497: "ℱ", + 8499: "ℳ", + 8500: "ℴ", 8501: "ℵ", + 8502: "ℶ", + 8503: "ℷ", + 8504: "ℸ", + 8517: "ⅅ", + 8518: "ⅆ", + 8519: "ⅇ", + 8520: "ⅈ", + 8531: "⅓", + 8532: "⅔", + 8533: "⅕", + 8534: "⅖", + 8535: "⅗", + 8536: "⅘", + 8537: "⅙", + 8538: "⅚", + 8539: "⅛", + 8540: "⅜", + 8541: "⅝", + 8542: "⅞", 8592: "←", 8593: "↑", 8594: "→", 8595: "↓", 8596: "↔", + 8597: "↕", + 8598: "↖", + 8599: "↗", + 8600: "↘", + 8601: "↙", + 8602: "↚", + 8603: "↛", + 8605: "↝", + 8606: "↞", + 8607: "↟", + 8608: "↠", + 8609: "↡", + 8610: "↢", + 8611: "↣", + 8612: "↤", + 8613: "↥", + 8614: "↦", + 8615: "↧", + 8617: "↩", + 8618: "↪", + 8619: "↫", + 8620: "↬", + 8621: "↭", + 8622: "↮", + 8624: "↰", + 8625: "↱", + 8626: "↲", + 8627: "↳", 8629: "↵", + 8630: "↶", + 8631: "↷", + 8634: "↺", + 8635: "↻", + 8636: "↼", + 8637: "↽", + 8638: "↾", + 8639: "↿", + 8640: "⇀", + 8641: "⇁", + 8642: "⇂", + 8643: "⇃", + 8644: "⇄", + 8645: "⇅", + 8646: "⇆", + 8647: "⇇", + 8648: "⇈", + 8649: "⇉", + 8650: "⇊", + 8651: "⇋", + 8652: "⇌;", + 8653: "⇍", + 8654: "⇎", + 8655: "⇏", 8656: "⇐", 8657: "⇑", 8658: "⇒", 8659: "⇓", 8660: "⇔", + 8661: "⇕", + 8662: "⇖", + 8663: "⇗", + 8664: "⇘", + 8665: "⇙", + 8666: "⇚", + 8667: "⇛", + 8669: "⇝", + 8676: "⇤", + 8677: "⇥", + 8693: "⇵", + 8701: "⇽", + 8702: "⇾", + 8703: "⇿", 8704: "∀", + 8705: "∁", 8706: "∂", 8707: "∃", + 8708: "∄", 8709: "∅", 8711: "∇", 8712: "∈", 8713: "∉", 8715: "∋", + 8716: "∌", 8719: "∏", + 8720: "∐", 8721: "∑", 8722: "−", + 8723: "∓", + 8724: "∔", + 8726: "∖", 8727: "∗", + 8728: "∘", 8730: "√", 8733: "∝", 8734: "∞", + 8735: "∟", 8736: "∠", + 8737: "∡", + 8738: "∢", + 8739: "∣", + 8740: "∤", + 8741: "∥", + 8742: "∦", 8743: "∧", 8744: "∨", 8745: "∩", 8746: "∪", 8747: "∫", + 8748: "∬", + 8749: "∭", + 8750: "∮", + 8751: "∯", + 8752: "∰", + 8753: "∱", + 8754: "∲", + 8755: "∳", 8756: "∴", + 8757: "∵", + 8758: "∶", + 8759: "∷", + 8760: "∸", + 8762: "∺", + 8763: "∻", 8764: "∼", + 8765: "∽", + 8766: "∾", + 8767: "∿", + 8768: "≀", + 8769: "≁", + 8770: "≂", + 8771: "≃", + 8772: "≄", 8773: "≅", + 8774: "≆", + 8775: "≇", 8776: "≈", + 8777: "≉", + 8778: "≊", + 8779: "≋", + 8780: "≌", + 8781: "≍", + 8782: "≎", + 8783: "≏", + 8784: "≐", + 8785: "≑", + 8786: "≒", + 8787: "≓", + 8788: "≔", + 8789: "≕", + 8790: "≖", + 8791: "≗", + 8793: "≙", + 8794: "≚", + 8796: "≜", + 8799: "≟", 8800: "≠", 8801: "≡", + 8802: "≢", 8804: "≤", 8805: "≥", + 8806: "≦", + 8807: "≧", + 8808: "≨", + 8809: "≩", + 8810: "≪", + 8811: "≫", + 8812: "≬", + 8813: "≭", + 8814: "≮", + 8815: "≯", + 8816: "≰", + 8817: "≱;", + 8818: "≲", + 8819: "≳", + 8820: "≴", + 8821: "≵", + 8822: "≶", + 8823: "≷", + 8824: "≸", + 8825: "≹", + 8826: "≺", + 8827: "≻", + 8828: "≼", + 8829: "≽", + 8830: "≾", + 8831: "≿", + 8832: "⊀", + 8833: "⊁", 8834: "⊂", 8835: "⊃", 8836: "⊄", + 8837: "⊅", 8838: "⊆", 8839: "⊇", + 8840: "⊈", + 8841: "⊉", + 8842: "⊊", + 8843: "⊋", + 8845: "⊍", + 8846: "⊎", + 8847: "⊏", + 8848: "⊐", + 8849: "⊑", + 8850: "⊒", + 8851: "⊓", + 8852: "⊔", 8853: "⊕", + 8854: "⊖", 8855: "⊗", + 8856: "⊘", + 8857: "⊙", + 8858: "⊚", + 8859: "⊛", + 8861: "⊝", + 8862: "⊞", + 8863: "⊟", + 8864: "⊠", + 8865: "⊡", + 8866: "⊢", + 8867: "⊣", + 8868: "⊤", 8869: "⊥", + 8871: "⊧", + 8872: "⊨", + 8873: "⊩", + 8874: "⊪", + 8875: "⊫", + 8876: "⊬", + 8877: "⊭", + 8878: "⊮", + 8879: "⊯", + 8880: "⊰", + 8882: "⊲", + 8883: "⊳", + 8884: "⊴", + 8885: "⊵", + 8886: "⊶", + 8887: "⊷", + 8888: "⊸", + 8889: "⊹", + 8890: "⊺", + 8891: "⊻", + 8893: "⊽", + 8894: "⊾", + 8895: "⊿", + 8896: "⋀", + 8897: "⋁", + 8898: "⋂", + 8899: "⋃", + 8900: "⋄", 8901: "⋅", + 8902: "⋆", + 8903: "⋇", + 8904: "⋈", + 8905: "⋉", + 8906: "⋊", + 8907: "⋋", + 8908: "⋌", + 8909: "⋍", + 8910: "⋎", + 8911: "⋏", + 8912: "⋐", + 8913: "⋑", + 8914: "⋒", + 8915: "⋓", + 8916: "⋔", + 8917: "⋕", + 8918: "⋖", + 8919: "⋗", + 8920: "⋘", + 8921: "⋙", + 8922: "⋚", + 8923: "⋛", + 8926: "⋞", + 8927: "⋟", + 8928: "⋠", + 8929: "⋡", + 8930: "⋢", + 8931: "⋣", + 8934: "⋦", + 8935: "⋧", + 8936: "⋨", + 8937: "⋩", + 8938: "⋪", + 8939: "⋫", + 8940: "⋬", + 8941: "⋭", 8942: "⋮", + 8943: "⋯", + 8944: "⋰", + 8945: "⋱", + 8946: "⋲", + 8947: "⋳", + 8948: "⋴", + 8949: "⋵", + 8950: "⋶", + 8951: "⋷", + 8953: "⋹", + 8954: "⋺", + 8955: "⋻", + 8956: "⋼", + 8957: "⋽", + 8958: "⋾", + 8965: "⌅", + 8966: "⌆", 8968: "⌈", 8969: "⌉", 8970: "⌊", 8971: "⌋", + 8972: "⌌", + 8973: "⌍", + 8974: "⌎", + 8975: "⌏", + 8976: "⌐", + 8978: "⌒", + 8979: "⌓", + 8981: "⌕", + 8982: "⌖", + 8988: "⌜", + 8989: "⌝", + 8990: "⌞", + 8991: "⌟", + 8994: "⌢", + 8995: "⌣", 9001: "⟨", 9002: "⟩", + 9005: "⌭", + 9006: "⌮", + 9014: "⌶", + 9021: "⌽", + 9023: "⌿", + 9084: "⍼", + 9136: "⎰", + 9137: "⎱", + 9140: "⎴", + 9141: "⎵", + 9142: "⎶", + 9180: "⏜", + 9181: "⏝", + 9182: "⏞", + 9183: "⏟", + 9186: "⏢", + 9191: "⏧", + 9251: "␣", + 9416: "Ⓢ", + 9472: "─", + 9474: "│", + 9484: "┌", + 9488: "┐", + 9492: "└", + 9496: "┘", + 9500: "├", + 9508: "┤", + 9516: "┬", + 9524: "┴", + 9532: "┼", + 9552: "═", + 9553: "║", + 9554: "╒", + 9555: "╓", + 9556: "╔", + 9557: "╕", + 9558: "╖", + 9559: "╗", + 9560: "╘", + 9561: "╙", + 9562: "╚", + 9563: "╛", + 9564: "╜", + 9565: "╝", + 9566: "╞", + 9567: "╟", + 9568: "╠", + 9569: "╡", + 9570: "╢", + 9571: "╣", + 9572: "╤", + 9573: "╥", 9674: "◊", + 9675: "○", + 9708: "◬", + 9711: "◯", + 9720: "◸", + 9721: "◹", + 9722: "◺", + 9723: "◻", + 9724: "◼", + 9733: "★", + 9734: "☆", + 9742: "☎", + 9792: "♀", + 9794: "♂", 9824: "♠", 9827: "♣", 9829: "♥", 9830: "♦", + 9834: "♪", + 9837: "♭", + 9838: "♮", + 9839: "♯", + 10003: "✓", + 10007: "✗", + 10016: "✠", + 10038: "✶", + 10072: "❘", + 10098: "❲", + 10099: "❳", + 10214: "⟦", + 10215: "⟧", + 10216: "⟨", + 10217: "⟩", + 10218: "⟪", + 10219: "⟫", + 10220: "⟬", + 10221: "⟭", + 10229: "⟵", + 10230: "⟶", + 10231: "⟷", + 10232: "⟸", + 10233: "⟹", + 10234: "⟺", + 10236: "⟼", + 10239: "⟿", + 10498: "⤂", + 10499: "⤃", + 10500: "⤄", + 10501: "⤅", + 10508: "⤌", + 10509: "⤍", + 10510: "⤎", + 10511: "⤏", + 10512: "⤐", + 10513: "⤑", + 10514: "⤒", + 10515: "⤓", + 10518: "⤖", + 10521: "⤙", + 10522: "⤚", + 10523: "⤛", + 10524: "⤜", + 10525: "⤝", + 10526: "⤞", + 10527: "⤟", + 10528: "⤠", + 10531: "⤣", + 10532: "⤤", + 10533: "⤥", + 10534: "⤦", + 10535: "⤧", + 10536: "⤨", + 10537: "⤩", + 10538: "⤪", + 10547: "⤳", + 10549: "⤵", + 10550: "⤶", + 10551: "⤷", + 10552: "⤸", + 10553: "⤹", + 10556: "⤼", + 10557: "⤽", + 10565: "⥅", + 10568: "⥈", + 10569: "⥉", + 10570: "⥊", + 10571: "⥋", + 10574: "⥎", + 10575: "⥏", + 10576: "⥐", + 10577: "⥑", + 10578: "⥒", + 10579: "⥓", + 10580: "⥔", + 10581: "⥕", + 10582: "⥖", + 10583: "⥗", + 10584: "⥘", + 10585: "⥙", + 10586: "⥚", + 10587: "⥛", + 10588: "⥜", + 10589: "⥝", + 10590: "⥞", + 10591: "⥟", + 10592: "⥠", + 10593: "⥡", + 10594: "⥢", + 10595: "⥣", + 10596: "⥤", + 10597: "⥥", + 10598: "⥦", + 10599: "⥧", + 10600: "⥨", + 10601: "⥩", + 10602: "⥪", + 10603: "⥫", + 10604: "⥬", + 10605: "⥭", + 10606: "⥮", + 10607: "⥯", + 10608: "⥰", + 10609: "⥱", + 10610: "⥲", + 10611: "⥳", + 10612: "⥴", + 10613: "⥵", + 10614: "⥶", + 10616: "⥸", + 10617: "⥹", + 10619: "⥻", + 10620: "⥼", + 10621: "⥽", + 10622: "⥾", + 10623: "⥿", + 10629: "⦅", + 10630: "⦆", + 10635: "⦋", + 10636: "⦌", + 10637: "⦍", + 10638: "⦎", + 10639: "⦏", + 10640: "⦐", + 10641: "⦑", + 10642: "⦒", + 10643: "⦓", + 10644: "⦔", + 10645: "⦕", + 10646: "⦖", + 10650: "⦚", + 10652: "⦜", + 10653: "⦝", + 10660: "⦤", + 10661: "⦥", + 10662: "⦦", + 10663: "⦧", + 10664: "⦨", + 10665: "⦩", + 10666: "⦪", + 10667: "⦫", + 10668: "⦬", + 10669: "⦭", + 10670: "⦮", + 10671: "⦯", + 10672: "⦰", + 10673: "⦱", + 10674: "⦲", + 10675: "⦳", + 10676: "⦴", + 10677: "⦵", + 10678: "⦶", + 10679: "⦷", + 10681: "⦹", + 10683: "⦻", + 10684: "⦼", + 10686: "⦾", + 10687: "⦿", + 10688: "⧀", + 10689: "⧁", + 10690: "⧂", + 10691: "⧃", + 10692: "⧄", + 10693: "⧅", + 10697: "⧉", + 10701: "⧍", + 10702: "⧎", + 10703: "⧏", + 10704: "⧐", + 10714: "∽̱", + 10716: "⧜", + 10717: "⧝", + 10718: "⧞", + 10723: "⧣", + 10724: "⧤", + 10725: "⧥", + 10731: "⧫", + 10740: "⧴", + 10742: "⧶", + 10752: "⨀", + 10753: "⨁", + 10754: "⨂", + 10756: "⨄", + 10758: "⨆", + 10764: "⨌", + 10765: "⨍", + 10768: "⨐", + 10769: "⨑", + 10770: "⨒", + 10771: "⨓", + 10772: "⨔", + 10773: "⨕", + 10774: "⨖", + 10775: "⨗", + 10786: "⨢", + 10787: "⨣", + 10788: "⨤", + 10789: "⨥", + 10790: "⨦", + 10791: "⨧", + 10793: "⨩", + 10794: "⨪", + 10797: "⨭", + 10798: "⨮", + 10799: "⨯", + 10800: "⨰", + 10801: "⨱", + 10803: "⨳", + 10804: "⨴", + 10805: "⨵", + 10806: "⨶", + 10807: "⨷", + 10808: "⨸", + 10809: "⨹", + 10810: "⨺", + 10811: "⨻", + 10812: "⨼", + 10815: "⨿", + 10816: "⩀", + 10818: "⩂", + 10819: "⩃", + 10820: "⩄", + 10821: "⩅", + 10822: "⩆", + 10823: "⩇", + 10824: "⩈", + 10825: "⩉", + 10826: "⩊", + 10827: "⩋", + 10828: "⩌", + 10829: "⩍", + 10832: "⩐", + 10835: "⩓", + 10836: "⩔", + 10837: "⩕", + 10838: "⩖", + 10839: "⩗", + 10840: "⩘", + 10842: "⩚", + 10843: "⩛", + 10844: "⩜", + 10845: "⩝", + 10847: "⩟", + 10854: "⩦", + 10858: "⩪", + 10861: "⩭", + 10862: "⩮", + 10863: "⩯", + 10864: "⩰", + 10865: "⩱", + 10866: "⩲", + 10867: "⩳", + 10868: "⩴", + 10869: "⩵", + 10871: "⩷", + 10872: "⩸", + 10873: "⩹", + 10874: "⩺", + 10875: "⩻", + 10876: "⩼", + 10877: "⩽", + 10878: "⩾", + 10879: "⩿", + 10880: "⪀", + 10881: "⪁", + 10882: "⪂", + 10883: "⪃", + 10884: "⪄", + 10885: "⪅", + 10886: "⪆", + 10887: "⪇", + 10888: "⪈", + 10889: "⪉", + 10890: "⪊", + 10891: "⪋", + 10892: "⪌", + 10893: "⪍", + 10894: "⪎", + 10895: "⪏", + 10896: "⪐", + 10897: "⪑", + 10898: "⪒", + 10899: "⪓", + 10900: "⪔", + 10901: "⪕", + 10902: "⪖", + 10903: "⪗", + 10904: "⪘", + 10905: "⪙", + 10906: "⪚", + 10909: "⪝", + 10910: "⪞", + 10911: "⪟", + 10912: "⪠", + 10913: "⪡", + 10914: "⪢", + 10916: "⪤", + 10917: "⪥", + 10918: "⪦", + 10919: "⪧", + 10920: "⪨", + 10921: "⪩", + 10922: "⪪", + 10923: "⪫", + 10924: "⪬", + 10925: "⪭", + 10926: "⪮", + 10927: "⪯", + 10928: "⪰", + 10931: "⪳", + 10932: "⪴", + 10933: "⪵", + 10934: "⪶", + 10935: "⪷", + 10936: "⪸", + 10937: "⪹", + 10938: "⪺", + 10939: "⪻", + 10940: "⪼", + 10941: "⪽", + 10942: "⪾", + 10943: "⪿", + 10944: "⫀", + 10945: "⫁", + 10946: "⫂", + 10947: "⫃", + 10948: "⫄", + 10949: "⫅", + 10950: "⫆", + 10951: "⫇", + 10952: "⫈", + 10955: "⫋", + 10956: "⫌", + 10959: "⫏", + 10960: "⫐", + 10961: "⫑", + 10962: "⫒", + 10963: "⫓", + 10964: "⫔", + 10965: "⫕", + 10966: "⫖", + 10967: "⫗", + 10968: "⫘", + 10969: "⫙", + 10970: "⫚", + 10971: "⫛", + 10980: "⫤", + 10982: "⫦", + 10983: "⫧", + 10984: "⫨", + 10985: "⫩", + 10987: "⫫", + 10988: "⫬", + 10989: "⫭", + 10990: "⫮", + 10991: "⫯", + 10992: "⫰", + 10993: "⫱", + 10994: "⫲", + 10995: "⫳", + 11005: "⫽", + 64256: "ff", + 64257: "fi", + 64258: "fl", + 64259: "ffi", + 64260: "ffl", + 119964: "𝒜", + 119966: "𝒞", + 119967: "𝒟", + 119970: "𝒢", + 119973: "𝒥", + 119974: "𝒦", + 119977: "𝒩", + 119978: "𝒪", + 119979: "𝒫", + 119980: "𝒬", + 119982: "𝒮", + 119983: "𝒯", + 119984: "𝒰", + 119985: "𝒱", + 119986: "𝒲", + 119987: "𝒳", + 119988: "𝒴", + 119989: "𝒵", + 119990: "𝒶", + 119991: "𝒷", + 119992: "𝒸", + 119993: "𝒹", + 119995: "𝒻", + 119997: "𝒽", + 119998: "𝒾", + 119999: "𝒿", + 120000: "𝓀", + 120001: "𝓁", + 120002: "𝓂", + 120003: "𝓃", + 120005: "𝓅", + 120006: "𝓆", + 120007: "𝓇", + 120008: "𝓈", + 120009: "𝓉", + 120010: "𝓊", + 120011: "𝓋", + 120012: "𝓌", + 120013: "𝓍", + 120014: "𝓎", + 120015: "𝓏", + 120068: "𝔄", + 120069: "𝔅", + 120071: "𝔇", + 120072: "𝔈", + 120073: "𝔉", + 120074: "𝔊", + 120077: "𝔍", + 120078: "𝔎", + 120079: "𝔏", + 120080: "𝔐", + 120081: "𝔑", + 120082: "𝔒", + 120083: "𝔓", + 120084: "𝔔", + 120086: "𝔖", + 120087: "𝔗", + 120088: "𝔘", + 120089: "𝔙", + 120090: "𝔚", + 120091: "𝔛", + 120092: "𝔜", + 120094: "𝔞", + 120095: "𝔟", + 120096: "𝔠", + 120097: "𝔡", + 120098: "𝔢", + 120099: "𝔣", + 120100: "𝔤", + 120101: "𝔥", + 120102: "𝔦", + 120103: "𝔧", + 120104: "𝔨", + 120105: "𝔩", + 120106: "𝔪", + 120107: "𝔫", + 120108: "𝔬", + 120109: "𝔭", + 120110: "𝔮", + 120111: "𝔯", + 120112: "𝔰", + 120113: "𝔱", + 120114: "𝔲", + 120115: "𝔳", + 120116: "𝔴", + 120117: "𝔵", + 120118: "𝔶", + 120119: "𝔷", + 120120: "𝔸", + 120121: "𝔹", + 120123: "𝔻", + 120124: "𝔼", + 120125: "𝔽", + 120126: "𝔾", + 120128: "𝕀", + 120129: "𝕁", + 120130: "𝕂", + 120131: "𝕃", + 120132: "𝕄", + 120134: "𝕆", + 120138: "𝕊", + 120139: "𝕋", + 120140: "𝕌", + 120141: "𝕍", + 120142: "𝕎", + 120143: "𝕏", + 120144: "𝕐", + 120146: "𝕒", + 120147: "𝕓", + 120148: "𝕔", + 120149: "𝕕", + 120150: "𝕖", + 120151: "𝕗", + 120152: "𝕘", + 120153: "𝕙", + 120154: "𝕚", + 120155: "𝕛", + 120156: "𝕜", + 120157: "𝕝", + 120158: "𝕞", + 120159: "𝕟", + 120160: "𝕠", + 120161: "𝕡", + 120162: "𝕢", + 120163: "𝕣", + 120164: "𝕤", + 120165: "𝕥", + 120166: "𝕦", + 120167: "𝕧", + 120168: "𝕨", + 120169: "𝕩", + 120170: "𝕪", + 120171: "𝕫" }; export default ToHTMLEntity; diff --git a/src/core/operations/ToHex.mjs b/src/core/operations/ToHex.mjs index a7035d4f..6ae48da9 100644 --- a/src/core/operations/ToHex.mjs +++ b/src/core/operations/ToHex.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {toHex, TO_HEX_DELIM_OPTIONS} from "../lib/Hex"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import {toHex, TO_HEX_DELIM_OPTIONS} from "../lib/Hex.mjs"; +import Utils from "../Utils.mjs"; /** * To Hex operation diff --git a/src/core/operations/ToHexContent.mjs b/src/core/operations/ToHexContent.mjs index 653c2224..9af09f24 100644 --- a/src/core/operations/ToHexContent.mjs +++ b/src/core/operations/ToHexContent.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {toHex} from "../lib/Hex"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {toHex} from "../lib/Hex.mjs"; /** * To Hex Content operation @@ -23,7 +23,7 @@ class ToHexContent extends Operation { this.module = "Default"; this.description = "Converts special characters in a string to hexadecimal. This format is used by SNORT for representing hex within ASCII text.

e.g. foo=bar becomes foo|3d|bar."; this.infoURL = "http://manual-snort-org.s3-website-us-east-1.amazonaws.com/node32.html#SECTION00451000000000000000"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { @@ -40,11 +40,12 @@ class ToHexContent extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { + input = new Uint8Array(input); const convert = args[0]; const spaces = args[1]; if (convert === "All chars") { diff --git a/src/core/operations/ToHexdump.mjs b/src/core/operations/ToHexdump.mjs index 0fbec5ce..ffe7131c 100644 --- a/src/core/operations/ToHexdump.mjs +++ b/src/core/operations/ToHexdump.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * To Hexdump operation diff --git a/src/core/operations/ToKebabCase.mjs b/src/core/operations/ToKebabCase.mjs index b7a15497..27a8ecac 100644 --- a/src/core/operations/ToKebabCase.mjs +++ b/src/core/operations/ToKebabCase.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import kebabCase from "lodash/kebabCase"; -import Operation from "../Operation"; -import { replaceVariableNames } from "../lib/Code"; +import kebabCase from "lodash/kebabCase.js"; +import Operation from "../Operation.mjs"; +import { replaceVariableNames } from "../lib/Code.mjs"; /** * To Kebab case operation diff --git a/src/core/operations/ToLowerCase.mjs b/src/core/operations/ToLowerCase.mjs index f28380bc..82a99a48 100644 --- a/src/core/operations/ToLowerCase.mjs +++ b/src/core/operations/ToLowerCase.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * To Lower case operation diff --git a/src/core/operations/ToMessagePack.mjs b/src/core/operations/ToMessagePack.mjs index 3de1daa7..b56e408d 100644 --- a/src/core/operations/ToMessagePack.mjs +++ b/src/core/operations/ToMessagePack.mjs @@ -4,9 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; import notepack from "notepack.io"; +import { isWorkerEnvironment } from "../Utils.mjs"; /** * To MessagePack operation @@ -35,7 +36,7 @@ class ToMessagePack extends Operation { */ run(input, args) { try { - if (ENVIRONMENT_IS_WORKER()) { + if (isWorkerEnvironment()) { return notepack.encode(input); } else { const res = notepack.encode(input); diff --git a/src/core/operations/ToMorseCode.mjs b/src/core/operations/ToMorseCode.mjs index 4770500b..c954c4b1 100644 --- a/src/core/operations/ToMorseCode.mjs +++ b/src/core/operations/ToMorseCode.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {LETTER_DELIM_OPTIONS, WORD_DELIM_OPTIONS} from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {LETTER_DELIM_OPTIONS, WORD_DELIM_OPTIONS} from "../lib/Delim.mjs"; /** * To Morse Code operation diff --git a/src/core/operations/ToOctal.mjs b/src/core/operations/ToOctal.mjs index 842b2b82..2ed8ef97 100644 --- a/src/core/operations/ToOctal.mjs +++ b/src/core/operations/ToOctal.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {DELIM_OPTIONS} from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {DELIM_OPTIONS} from "../lib/Delim.mjs"; /** diff --git a/src/core/operations/ToPunycode.mjs b/src/core/operations/ToPunycode.mjs index 6cf5096c..0e579079 100644 --- a/src/core/operations/ToPunycode.mjs +++ b/src/core/operations/ToPunycode.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import punycode from "punycode"; /** diff --git a/src/core/operations/ToQuotedPrintable.mjs b/src/core/operations/ToQuotedPrintable.mjs index 51044021..e26f3c67 100644 --- a/src/core/operations/ToQuotedPrintable.mjs +++ b/src/core/operations/ToQuotedPrintable.mjs @@ -8,7 +8,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * To Quoted Printable operation @@ -25,17 +25,18 @@ class ToQuotedPrintable extends Operation { this.module = "Default"; this.description = "Quoted-Printable, or QP encoding, is an encoding using printable ASCII characters (alphanumeric and the equals sign '=') to transmit 8-bit data over a 7-bit data path or, generally, over a medium which is not 8-bit clean. It is defined as a MIME content transfer encoding for use in e-mail.

QP works by using the equals sign '=' as an escape character. It also limits line length to 76, as some software has limits on line length."; this.infoURL = "https://wikipedia.org/wiki/Quoted-printable"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = []; } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { + input = new Uint8Array(input); let mimeEncodedStr = this.mimeEncode(input); // fix line breaks @@ -73,7 +74,7 @@ class ToQuotedPrintable extends Operation { /** * Encodes mime data. * - * @param {byteArray} buffer + * @param {byteArray|Uint8Array} buffer * @returns {string} */ mimeEncode(buffer) { diff --git a/src/core/operations/ToSnakeCase.mjs b/src/core/operations/ToSnakeCase.mjs index 0cef88fe..5cb566af 100644 --- a/src/core/operations/ToSnakeCase.mjs +++ b/src/core/operations/ToSnakeCase.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import snakeCase from "lodash/snakeCase"; -import Operation from "../Operation"; -import { replaceVariableNames } from "../lib/Code"; +import snakeCase from "lodash/snakeCase.js"; +import Operation from "../Operation.mjs"; +import { replaceVariableNames } from "../lib/Code.mjs"; /** * To Snake case operation diff --git a/src/core/operations/ToTable.mjs b/src/core/operations/ToTable.mjs index 6d10b340..91b07771 100644 --- a/src/core/operations/ToTable.mjs +++ b/src/core/operations/ToTable.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * To Table operation diff --git a/src/core/operations/ToUNIXTimestamp.mjs b/src/core/operations/ToUNIXTimestamp.mjs index d8cde848..3eaf2ba2 100644 --- a/src/core/operations/ToUNIXTimestamp.mjs +++ b/src/core/operations/ToUNIXTimestamp.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import moment from "moment-timezone"; -import {UNITS} from "../lib/DateTime"; -import OperationError from "../errors/OperationError"; +import {UNITS} from "../lib/DateTime.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * To UNIX Timestamp operation diff --git a/src/core/operations/ToUpperCase.mjs b/src/core/operations/ToUpperCase.mjs index d56aacff..0bc9b5a9 100644 --- a/src/core/operations/ToUpperCase.mjs +++ b/src/core/operations/ToUpperCase.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * To Upper case operation diff --git a/src/core/operations/TranslateDateTimeFormat.mjs b/src/core/operations/TranslateDateTimeFormat.mjs index 08f2d646..65b4e1fe 100644 --- a/src/core/operations/TranslateDateTimeFormat.mjs +++ b/src/core/operations/TranslateDateTimeFormat.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import moment from "moment-timezone"; -import {DATETIME_FORMATS, FORMAT_EXAMPLES} from "../lib/DateTime"; +import {DATETIME_FORMATS, FORMAT_EXAMPLES} from "../lib/DateTime.mjs"; /** * Translate DateTime Format operation @@ -61,7 +61,7 @@ class TranslateDateTimeFormat extends Operation { * @returns {html} */ run(input, args) { - const [inputFormat, inputTimezone, outputFormat, outputTimezone] = args.splice(1); + const [inputFormat, inputTimezone, outputFormat, outputTimezone] = args.slice(1); let date; try { diff --git a/src/core/operations/TripleDESDecrypt.mjs b/src/core/operations/TripleDESDecrypt.mjs index 47d1df05..90a83220 100644 --- a/src/core/operations/TripleDESDecrypt.mjs +++ b/src/core/operations/TripleDESDecrypt.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; import forge from "node-forge/dist/forge.min.js"; /** diff --git a/src/core/operations/TripleDESEncrypt.mjs b/src/core/operations/TripleDESEncrypt.mjs index e7eb6bca..d4475f83 100644 --- a/src/core/operations/TripleDESEncrypt.mjs +++ b/src/core/operations/TripleDESEncrypt.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; import forge from "node-forge/dist/forge.min.js"; /** diff --git a/src/core/operations/Typex.mjs b/src/core/operations/Typex.mjs index 70b5f6c3..a9b7a3d0 100644 --- a/src/core/operations/Typex.mjs +++ b/src/core/operations/Typex.mjs @@ -6,10 +6,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import {LETTERS, Reflector} from "../lib/Enigma"; -import {ROTORS, REFLECTORS, TypexMachine, Plugboard, Rotor} from "../lib/Typex"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {LETTERS, Reflector} from "../lib/Enigma.mjs"; +import {ROTORS, REFLECTORS, TypexMachine, Plugboard, Rotor} from "../lib/Typex.mjs"; /** * Typex operation diff --git a/src/core/operations/UNIXTimestampToWindowsFiletime.mjs b/src/core/operations/UNIXTimestampToWindowsFiletime.mjs index 187b98ab..b93c881f 100644 --- a/src/core/operations/UNIXTimestampToWindowsFiletime.mjs +++ b/src/core/operations/UNIXTimestampToWindowsFiletime.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import BigNumber from "bignumber.js"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * UNIX Timestamp to Windows Filetime operation diff --git a/src/core/operations/URLDecode.mjs b/src/core/operations/URLDecode.mjs index dbbee20d..29f60623 100644 --- a/src/core/operations/URLDecode.mjs +++ b/src/core/operations/URLDecode.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * URL Decode operation diff --git a/src/core/operations/URLEncode.mjs b/src/core/operations/URLEncode.mjs index 2eb5b621..f819ee2e 100644 --- a/src/core/operations/URLEncode.mjs +++ b/src/core/operations/URLEncode.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * URL Encode operation diff --git a/src/core/operations/UnescapeString.mjs b/src/core/operations/UnescapeString.mjs index 62eab48e..8fafb446 100644 --- a/src/core/operations/UnescapeString.mjs +++ b/src/core/operations/UnescapeString.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * Unescape string operation @@ -20,7 +20,7 @@ class UnescapeString extends Operation { this.name = "Unescape string"; this.module = "Default"; - this.description = "Unescapes characters in a string that have been escaped. For example, Don\\'t stop me now becomes Don't stop me now.

Supports the following escape sequences:
  • \\n (Line feed/newline)
  • \\r (Carriage return)
  • \\t (Horizontal tab)
  • \\b (Backspace)
  • \\f (Form feed)
  • \\xnn (Hex, where n is 0-f)
  • \\\\ (Backslash)
  • \\' (Single quote)
  • \\" (Double quote)
  • \\unnnn (Unicode character)
  • \\u{nnnnnn} (Unicode code point)
"; + this.description = "Unescapes characters in a string that have been escaped. For example, Don\\'t stop me now becomes Don't stop me now.

Supports the following escape sequences:
  • \\n (Line feed/newline)
  • \\r (Carriage return)
  • \\t (Horizontal tab)
  • \\b (Backspace)
  • \\f (Form feed)
  • \\nnn (Octal, where n is 0-7)
  • \\xnn (Hex, where n is 0-f)
  • \\\\ (Backslash)
  • \\' (Single quote)
  • \\" (Double quote)
  • \\unnnn (Unicode character)
  • \\u{nnnnnn} (Unicode code point)
"; this.infoURL = "https://wikipedia.org/wiki/Escape_sequence"; this.inputType = "string"; this.outputType = "string"; diff --git a/src/core/operations/UnescapeUnicodeCharacters.mjs b/src/core/operations/UnescapeUnicodeCharacters.mjs index ab8af2a8..c639613e 100644 --- a/src/core/operations/UnescapeUnicodeCharacters.mjs +++ b/src/core/operations/UnescapeUnicodeCharacters.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; /** * Unescape Unicode Characters operation diff --git a/src/core/operations/Unique.mjs b/src/core/operations/Unique.mjs index 6848968b..89de74c2 100644 --- a/src/core/operations/Unique.mjs +++ b/src/core/operations/Unique.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {INPUT_DELIM_OPTIONS} from "../lib/Delim"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {INPUT_DELIM_OPTIONS} from "../lib/Delim.mjs"; /** * Unique operation diff --git a/src/core/operations/Untar.mjs b/src/core/operations/Untar.mjs index 8655ba68..78a469ce 100644 --- a/src/core/operations/Untar.mjs +++ b/src/core/operations/Untar.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import Stream from "../lib/Stream"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import Stream from "../lib/Stream.mjs"; /** * Untar operation @@ -23,7 +23,7 @@ class Untar extends Operation { this.module = "Compression"; this.description = "Unpacks a tarball and displays it per file."; this.infoURL = "https://wikipedia.org/wiki/Tar_(computing)"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "List"; this.presentType = "html"; this.args = []; @@ -37,11 +37,12 @@ class Untar extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {List} */ run(input, args) { + input = new Uint8Array(input); const stream = new Stream(input), files = []; diff --git a/src/core/operations/Unzip.mjs b/src/core/operations/Unzip.mjs index 2766c215..3bca9401 100644 --- a/src/core/operations/Unzip.mjs +++ b/src/core/operations/Unzip.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import unzip from "zlibjs/bin/unzip.min"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import unzip from "zlibjs/bin/unzip.min.js"; const Zlib = unzip.Zlib; diff --git a/src/core/operations/VarIntDecode.mjs b/src/core/operations/VarIntDecode.mjs index df570be9..46c99cb7 100644 --- a/src/core/operations/VarIntDecode.mjs +++ b/src/core/operations/VarIntDecode.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import Protobuf from "../lib/Protobuf"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Protobuf from "../lib/Protobuf.mjs"; /** * VarInt Decode operation diff --git a/src/core/operations/VarIntEncode.mjs b/src/core/operations/VarIntEncode.mjs index 69a552f6..d86d33f5 100644 --- a/src/core/operations/VarIntEncode.mjs +++ b/src/core/operations/VarIntEncode.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import Protobuf from "../lib/Protobuf"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Protobuf from "../lib/Protobuf.mjs"; /** * VarInt Encode operation diff --git a/src/core/operations/VigenèreDecode.mjs b/src/core/operations/VigenèreDecode.mjs index 98aef876..90cca3f8 100644 --- a/src/core/operations/VigenèreDecode.mjs +++ b/src/core/operations/VigenèreDecode.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Vigenère Decode operation */ diff --git a/src/core/operations/VigenèreEncode.mjs b/src/core/operations/VigenèreEncode.mjs index 2e02339c..db212fe2 100644 --- a/src/core/operations/VigenèreEncode.mjs +++ b/src/core/operations/VigenèreEncode.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * Vigenère Encode operation diff --git a/src/core/operations/Whirlpool.mjs b/src/core/operations/Whirlpool.mjs index 1ef8a11c..0e1c51ab 100644 --- a/src/core/operations/Whirlpool.mjs +++ b/src/core/operations/Whirlpool.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {runHash} from "../lib/Hash"; +import Operation from "../Operation.mjs"; +import {runHash} from "../lib/Hash.mjs"; /** * Whirlpool operation diff --git a/src/core/operations/WindowsFiletimeToUNIXTimestamp.mjs b/src/core/operations/WindowsFiletimeToUNIXTimestamp.mjs index f5b59037..18542bee 100644 --- a/src/core/operations/WindowsFiletimeToUNIXTimestamp.mjs +++ b/src/core/operations/WindowsFiletimeToUNIXTimestamp.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; import BigNumber from "bignumber.js"; -import OperationError from "../errors/OperationError"; +import OperationError from "../errors/OperationError.mjs"; /** * Windows Filetime to UNIX Timestamp operation diff --git a/src/core/operations/XKCDRandomNumber.mjs b/src/core/operations/XKCDRandomNumber.mjs index e5811549..ba9b5e27 100644 --- a/src/core/operations/XKCDRandomNumber.mjs +++ b/src/core/operations/XKCDRandomNumber.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * XKCD Random Number operation diff --git a/src/core/operations/XMLBeautify.mjs b/src/core/operations/XMLBeautify.mjs index 4c059411..2cabb9b2 100644 --- a/src/core/operations/XMLBeautify.mjs +++ b/src/core/operations/XMLBeautify.mjs @@ -5,7 +5,7 @@ */ import vkbeautify from "vkbeautify"; -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * XML Beautify operation diff --git a/src/core/operations/XMLMinify.mjs b/src/core/operations/XMLMinify.mjs index 9c4fb2f6..5c710951 100644 --- a/src/core/operations/XMLMinify.mjs +++ b/src/core/operations/XMLMinify.mjs @@ -5,7 +5,7 @@ */ import vkbeautify from "vkbeautify"; -import Operation from "../Operation"; +import Operation from "../Operation.mjs"; /** * XML Minify operation diff --git a/src/core/operations/XOR.mjs b/src/core/operations/XOR.mjs index 3bd508fc..aa228842 100644 --- a/src/core/operations/XOR.mjs +++ b/src/core/operations/XOR.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import { bitOp, xor, BITWISE_OP_DELIMS } from "../lib/BitwiseOp"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import { bitOp, xor, BITWISE_OP_DELIMS } from "../lib/BitwiseOp.mjs"; /** * XOR operation @@ -23,7 +23,7 @@ class XOR extends Operation { this.module = "Default"; this.description = "XOR the input with the given key.
e.g. fe023da5

Options
Null preserving: If the current byte is 0x00 or the same as the key, skip it.

Scheme:
  • Standard - key is unchanged after each round
  • Input differential - key is set to the value of the previous unprocessed byte
  • Output differential - key is set to the value of the previous processed byte
  • Cascade - key is set to the input byte shifted by one
"; this.infoURL = "https://wikipedia.org/wiki/XOR"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "byteArray"; this.args = [ { @@ -46,11 +46,12 @@ class XOR extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {byteArray} */ run(input, args) { + input = new Uint8Array(input); const key = Utils.convertToByteArray(args[0].string || "", args[0].option), [, scheme, nullPreserving] = args; diff --git a/src/core/operations/XORBruteForce.mjs b/src/core/operations/XORBruteForce.mjs index 6284793f..9b548df8 100644 --- a/src/core/operations/XORBruteForce.mjs +++ b/src/core/operations/XORBruteForce.mjs @@ -4,10 +4,11 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import { bitOp, xor } from "../lib/BitwiseOp"; -import { toHex } from "../lib/Hex"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import { bitOp, xor } from "../lib/BitwiseOp.mjs"; +import { toHex } from "../lib/Hex.mjs"; +import { isWorkerEnvironment } from "../Utils.mjs"; /** * XOR Brute Force operation @@ -24,7 +25,7 @@ class XORBruteForce extends Operation { this.module = "Default"; this.description = "Enumerate all possible XOR solutions. Current maximum key length is 2 due to browser performance.

Optionally enter a string that you expect to find in the plaintext to filter results (crib)."; this.infoURL = "https://wikipedia.org/wiki/Exclusive_or"; - this.inputType = "byteArray"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { @@ -71,11 +72,12 @@ class XORBruteForce extends Operation { } /** - * @param {byteArray} input + * @param {ArrayBuffer} input * @param {Object[]} args * @returns {string} */ run(input, args) { + input = new Uint8Array(input); const [ keyLength, sampleLength, @@ -94,7 +96,7 @@ class XORBruteForce extends Operation { input = input.slice(sampleOffset, sampleOffset + sampleLength); - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Calculating " + Math.pow(256, keyLength) + " values..."); /** @@ -114,7 +116,7 @@ class XORBruteForce extends Operation { }; for (let key = 1, l = Math.pow(256, keyLength); key < l; key++) { - if (key % 10000 === 0 && ENVIRONMENT_IS_WORKER()) { + if (key % 10000 === 0 && isWorkerEnvironment()) { self.sendStatusMessage("Calculating " + l + " values... " + Math.floor(key / l * 100) + "%"); } diff --git a/src/core/operations/XPathExpression.mjs b/src/core/operations/XPathExpression.mjs index 0cca73b7..3b724ce0 100644 --- a/src/core/operations/XPathExpression.mjs +++ b/src/core/operations/XPathExpression.mjs @@ -4,8 +4,8 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; import xmldom from "xmldom"; import xpath from "xpath"; diff --git a/src/core/operations/YARARules.mjs b/src/core/operations/YARARules.mjs index 91a27963..b95d5a4c 100644 --- a/src/core/operations/YARARules.mjs +++ b/src/core/operations/YARARules.mjs @@ -4,9 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; import Yara from "libyara-wasm"; +import { isWorkerEnvironment } from "../Utils.mjs"; /** * YARA Rules operation @@ -61,21 +62,21 @@ class YARARules extends Operation { * @returns {string} */ run(input, args) { - if (ENVIRONMENT_IS_WORKER()) + if (isWorkerEnvironment()) self.sendStatusMessage("Instantiating YARA..."); 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."); + if (isWorkerEnvironment()) self.sendStatusMessage("Converting data for YARA."); let matchString = ""; const inpArr = new Uint8Array(input); // Turns out embind knows that JS uint8array <==> C++ std::string - if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Running YARA matching."); + if (isWorkerEnvironment()) self.sendStatusMessage("Running YARA matching."); const resp = yara.run(inpArr, rules); - if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Processing data."); + if (isWorkerEnvironment()) self.sendStatusMessage("Processing data."); if (resp.compileErrors.size() > 0) { for (let i = 0; i < resp.compileErrors.size(); i++) { diff --git a/src/core/operations/Zip.mjs b/src/core/operations/Zip.mjs index 54a92726..dbe51462 100644 --- a/src/core/operations/Zip.mjs +++ b/src/core/operations/Zip.mjs @@ -4,10 +4,10 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import Utils from "../Utils"; -import {COMPRESSION_TYPE, ZLIB_COMPRESSION_TYPE_LOOKUP} from "../lib/Zlib"; -import zip from "zlibjs/bin/zip.min"; +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import {COMPRESSION_TYPE, ZLIB_COMPRESSION_TYPE_LOOKUP} from "../lib/Zlib.mjs"; +import zip from "zlibjs/bin/zip.min.js"; const Zlib = zip.Zlib; diff --git a/src/core/operations/ZlibDeflate.mjs b/src/core/operations/ZlibDeflate.mjs index d4685704..4436d84a 100644 --- a/src/core/operations/ZlibDeflate.mjs +++ b/src/core/operations/ZlibDeflate.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {COMPRESSION_TYPE, ZLIB_COMPRESSION_TYPE_LOOKUP} from "../lib/Zlib"; -import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min"; +import Operation from "../Operation.mjs"; +import {COMPRESSION_TYPE, ZLIB_COMPRESSION_TYPE_LOOKUP} from "../lib/Zlib.mjs"; +import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min.js"; const Zlib = zlibAndGzip.Zlib; diff --git a/src/core/operations/ZlibInflate.mjs b/src/core/operations/ZlibInflate.mjs index a796cb3e..9f715c06 100644 --- a/src/core/operations/ZlibInflate.mjs +++ b/src/core/operations/ZlibInflate.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import Operation from "../Operation"; -import {INFLATE_BUFFER_TYPE} from "../lib/Zlib"; -import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min"; +import Operation from "../Operation.mjs"; +import {INFLATE_BUFFER_TYPE} from "../lib/Zlib.mjs"; +import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min.js"; const Zlib = zlibAndGzip.Zlib; diff --git a/src/core/vendor/bzip2.mjs b/src/core/vendor/bzip2.mjs deleted file mode 100755 index df0573d8..00000000 --- a/src/core/vendor/bzip2.mjs +++ /dev/null @@ -1,265 +0,0 @@ -/** @license -======================================================================== - bzip2.js - a small bzip2 decompression implementation - - Copyright 2011 by antimatter15 (antimatter15@gmail.com) - - Based on micro-bunzip by Rob Landley (rob@landley.net). - - Copyright (c) 2011 by antimatter15 (antimatter15@gmail.com). - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH - THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ -"use strict"; - -var bzip2 = {}; - -bzip2.array = function(bytes){ - var bit = 0, byte = 0; - var BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF ]; - return function(n){ - var result = 0; - while(n > 0){ - var left = 8 - bit; - if(n >= left){ - result <<= left; - result |= (BITMASK[left] & bytes[byte++]); - bit = 0; - n -= left; - }else{ - result <<= n; - result |= ((bytes[byte] & (BITMASK[n] << (8 - n - bit))) >> (8 - n - bit)); - bit += n; - n = 0; - } - } - return result - } -} - -bzip2.simple = function(bits){ - var size = bzip2.header(bits); - var all = '', chunk = ''; - do{ - all += chunk; - chunk = bzip2.decompress(bits, size); - }while(chunk != -1); - return all; -} - -bzip2.header = function(bits){ - if(bits(8*3) != 4348520) throw "No magic number found"; - var i = bits(8) - 48; - if(i < 1 || i > 9) throw "Not a BZIP archive"; - return i; -}; - - -//takes a function for reading the block data (starting with 0x314159265359) -//a block size (0-9) (optional, defaults to 9) -//a length at which to stop decompressing and return the output -bzip2.decompress = function(bits, size, len){ - var MAX_HUFCODE_BITS = 20; - var MAX_SYMBOLS = 258; - var SYMBOL_RUNA = 0; - var SYMBOL_RUNB = 1; - var GROUP_SIZE = 50; - - var bufsize = 100000 * size; - for(var h = '', i = 0; i < 6; i++) h += bits(8).toString(16); - if(h == "177245385090") return -1; //last block - if(h != "314159265359") throw "Not valid bzip data"; - bits(32); //ignore CRC codes - if(bits(1)) throw "Unsupported obsolete version"; - var origPtr = bits(24); - if(origPtr > bufsize) throw "Initial position larger than buffer size"; - var t = bits(16); - var symToByte = new Uint8Array(256), - symTotal = 0; - for (i = 0; i < 16; i++) { - if(t & (1 << (15 - i))) { - var k = bits(16); - for(j = 0; j < 16; j++){ - if(k & (1 << (15 - j))){ - symToByte[symTotal++] = (16 * i) + j; - } - } - } - } - - var groupCount = bits(3); - if(groupCount < 2 || groupCount > 6) throw "Error 1"; - var nSelectors = bits(15); - if(nSelectors == 0) throw "Error"; - var mtfSymbol = []; //TODO: possibly replace JS array with typed arrays - for(var i = 0; i < groupCount; i++) mtfSymbol[i] = i; - var selectors = new Uint8Array(32768); - - for(var i = 0; i < nSelectors; i++){ - for(var j = 0; bits(1); j++) if(j >= groupCount) throw "Error 2"; - var uc = mtfSymbol[j]; - mtfSymbol.splice(j, 1); //this is a probably inefficient MTF transform - mtfSymbol.splice(0, 0, uc); - selectors[i] = uc; - } - - var symCount = symTotal + 2; - var groups = []; - for(var j = 0; j < groupCount; j++){ - var length = new Uint8Array(MAX_SYMBOLS), - temp = new Uint8Array(MAX_HUFCODE_BITS+1); - t = bits(5); //lengths - for(var i = 0; i < symCount; i++){ - while(true){ - if (t < 1 || t > MAX_HUFCODE_BITS) throw "Error 3"; - if(!bits(1)) break; - if(!bits(1)) t++; - else t--; - } - length[i] = t; - } - var minLen, maxLen; - minLen = maxLen = length[0]; - for(var i = 1; i < symCount; i++){ - if(length[i] > maxLen) maxLen = length[i]; - else if(length[i] < minLen) minLen = length[i]; - } - var hufGroup; - hufGroup = groups[j] = {}; - hufGroup.permute = new Uint32Array(MAX_SYMBOLS); - hufGroup.limit = new Uint32Array(MAX_HUFCODE_BITS + 1); - hufGroup.base = new Uint32Array(MAX_HUFCODE_BITS + 1); - hufGroup.minLen = minLen; - hufGroup.maxLen = maxLen; - var base = hufGroup.base.subarray(1); - var limit = hufGroup.limit.subarray(1); - var pp = 0; - for(var i = minLen; i <= maxLen; i++) - for(var t = 0; t < symCount; t++) - if(length[t] == i) hufGroup.permute[pp++] = t; - for(i = minLen; i <= maxLen; i++) temp[i] = limit[i] = 0; - for(i = 0; i < symCount; i++) temp[length[i]]++; - pp = t = 0; - for(i = minLen; i < maxLen; i++) { - pp += temp[i]; - limit[i] = pp - 1; - pp <<= 1; - base[i+1] = pp - (t += temp[i]); - } - limit[maxLen]=pp+temp[maxLen]-1; - base[minLen]=0; - } - var byteCount = new Uint32Array(256); - for(var i = 0; i < 256; i++) mtfSymbol[i] = i; - var runPos, count, symCount, selector; - runPos = count = symCount = selector = 0; - var buf = new Uint32Array(bufsize); - while(true){ - if(!(symCount--)){ - symCount = GROUP_SIZE - 1; - if(selector >= nSelectors) throw "Error 4"; - hufGroup = groups[selectors[selector++]]; - base = hufGroup.base.subarray(1); - limit = hufGroup.limit.subarray(1); - } - i = hufGroup.minLen; - j = bits(i); - while(true){ - if(i > hufGroup.maxLen) throw "Error 5"; - if(j <= limit[i]) break; - i++; - j = (j << 1) | bits(1); - } - j -= base[i]; - if(j < 0 || j >= MAX_SYMBOLS) throw "Error 6"; - var nextSym = hufGroup.permute[j]; - if (nextSym == SYMBOL_RUNA || nextSym == SYMBOL_RUNB) { - if(!runPos){ - runPos = 1; - t = 0; - } - if(nextSym == SYMBOL_RUNA) t += runPos; - else t += 2 * runPos; - runPos <<= 1; - continue; - } - if(runPos){ - runPos = 0; - if(count + t >= bufsize) throw "Error 7"; - uc = symToByte[mtfSymbol[0]]; - byteCount[uc] += t; - while(t--) buf[count++] = uc; - } - if(nextSym > symTotal) break; - if(count >= bufsize) throw "Error 8"; - i = nextSym -1; - uc = mtfSymbol[i]; - mtfSymbol.splice(i, 1); - mtfSymbol.splice(0, 0, uc); - uc = symToByte[uc]; - byteCount[uc]++; - buf[count++] = uc; - } - if(origPtr < 0 || origPtr >= count) throw "Error 9"; - var j = 0; - for(var i = 0; i < 256; i++){ - k = j + byteCount[i]; - byteCount[i] = j; - j = k; - } - for(var i = 0; i < count; i++){ - uc = buf[i] & 0xff; - buf[byteCount[uc]] |= (i << 8); - byteCount[uc]++; - } - var pos = 0, current = 0, run = 0; - if(count) { - pos = buf[origPtr]; - current = (pos & 0xff); - pos >>= 8; - run = -1; - } - count = count; - var output = ''; - var copies, previous, outbyte; - if(!len) len = Infinity; - while(count){ - count--; - previous = current; - pos = buf[pos]; - current = pos & 0xff; - pos >>= 8; - if(run++ == 3){ - copies = current; - outbyte = previous; - current = -1; - }else{ - copies = 1; - outbyte = current; - } - while(copies--){ - output += (String.fromCharCode(outbyte)); - if(!--len) return output; - } - if(current != previous) run = 0; - } - return output; -} - -export default bzip2; diff --git a/src/core/vendor/gost/gostCipher.mjs b/src/core/vendor/gost/gostCipher.mjs new file mode 100644 index 00000000..18883c1f --- /dev/null +++ b/src/core/vendor/gost/gostCipher.mjs @@ -0,0 +1,2259 @@ +/** + * GOST 28147-89/GOST R 34.12-2015/GOST R 32.13-2015 Encryption Algorithm + * 1.76 + * 2014-2016, Rudolf Nickolaev. All rights reserved. + * + * Exported for CyberChef by mshwed [m@ttshwed.com] + */ + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +import GostRandom from './gostRandom.mjs'; + +import crypto from 'crypto' + +/* +* Initial parameters and common algortithms of GOST 28147-89 +* +* http://tools.ietf.org/html/rfc5830 +* +*/ // + +var root = {}; +var rootCrypto = crypto; +var CryptoOperationData = ArrayBuffer; +var SyntaxError = Error, + DataError = Error, + NotSupportedError = Error; +/* +* Check supported +* This implementation support only Little Endian arhitecture +*/ + +var littleEndian = (function () { + var buffer = new CryptoOperationData(2); + new DataView(buffer).setInt16(0, 256, true); + return new Int16Array(buffer)[0] === 256; +})(); + +// Default initial vector +var defaultIV = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); + +// Predefined sBox collection +var sBoxes = { + 'E-TEST': [ + 0x4, 0x2, 0xF, 0x5, 0x9, 0x1, 0x0, 0x8, 0xE, 0x3, 0xB, 0xC, 0xD, 0x7, 0xA, 0x6, + 0xC, 0x9, 0xF, 0xE, 0x8, 0x1, 0x3, 0xA, 0x2, 0x7, 0x4, 0xD, 0x6, 0x0, 0xB, 0x5, + 0xD, 0x8, 0xE, 0xC, 0x7, 0x3, 0x9, 0xA, 0x1, 0x5, 0x2, 0x4, 0x6, 0xF, 0x0, 0xB, + 0xE, 0x9, 0xB, 0x2, 0x5, 0xF, 0x7, 0x1, 0x0, 0xD, 0xC, 0x6, 0xA, 0x4, 0x3, 0x8, + 0x3, 0xE, 0x5, 0x9, 0x6, 0x8, 0x0, 0xD, 0xA, 0xB, 0x7, 0xC, 0x2, 0x1, 0xF, 0x4, + 0x8, 0xF, 0x6, 0xB, 0x1, 0x9, 0xC, 0x5, 0xD, 0x3, 0x7, 0xA, 0x0, 0xE, 0x2, 0x4, + 0x9, 0xB, 0xC, 0x0, 0x3, 0x6, 0x7, 0x5, 0x4, 0x8, 0xE, 0xF, 0x1, 0xA, 0x2, 0xD, + 0xC, 0x6, 0x5, 0x2, 0xB, 0x0, 0x9, 0xD, 0x3, 0xE, 0x7, 0xA, 0xF, 0x4, 0x1, 0x8 + ], + 'E-A': [ + 0x9, 0x6, 0x3, 0x2, 0x8, 0xB, 0x1, 0x7, 0xA, 0x4, 0xE, 0xF, 0xC, 0x0, 0xD, 0x5, + 0x3, 0x7, 0xE, 0x9, 0x8, 0xA, 0xF, 0x0, 0x5, 0x2, 0x6, 0xC, 0xB, 0x4, 0xD, 0x1, + 0xE, 0x4, 0x6, 0x2, 0xB, 0x3, 0xD, 0x8, 0xC, 0xF, 0x5, 0xA, 0x0, 0x7, 0x1, 0x9, + 0xE, 0x7, 0xA, 0xC, 0xD, 0x1, 0x3, 0x9, 0x0, 0x2, 0xB, 0x4, 0xF, 0x8, 0x5, 0x6, + 0xB, 0x5, 0x1, 0x9, 0x8, 0xD, 0xF, 0x0, 0xE, 0x4, 0x2, 0x3, 0xC, 0x7, 0xA, 0x6, + 0x3, 0xA, 0xD, 0xC, 0x1, 0x2, 0x0, 0xB, 0x7, 0x5, 0x9, 0x4, 0x8, 0xF, 0xE, 0x6, + 0x1, 0xD, 0x2, 0x9, 0x7, 0xA, 0x6, 0x0, 0x8, 0xC, 0x4, 0x5, 0xF, 0x3, 0xB, 0xE, + 0xB, 0xA, 0xF, 0x5, 0x0, 0xC, 0xE, 0x8, 0x6, 0x2, 0x3, 0x9, 0x1, 0x7, 0xD, 0x4 + ], + 'E-B': [ + 0x8, 0x4, 0xB, 0x1, 0x3, 0x5, 0x0, 0x9, 0x2, 0xE, 0xA, 0xC, 0xD, 0x6, 0x7, 0xF, + 0x0, 0x1, 0x2, 0xA, 0x4, 0xD, 0x5, 0xC, 0x9, 0x7, 0x3, 0xF, 0xB, 0x8, 0x6, 0xE, + 0xE, 0xC, 0x0, 0xA, 0x9, 0x2, 0xD, 0xB, 0x7, 0x5, 0x8, 0xF, 0x3, 0x6, 0x1, 0x4, + 0x7, 0x5, 0x0, 0xD, 0xB, 0x6, 0x1, 0x2, 0x3, 0xA, 0xC, 0xF, 0x4, 0xE, 0x9, 0x8, + 0x2, 0x7, 0xC, 0xF, 0x9, 0x5, 0xA, 0xB, 0x1, 0x4, 0x0, 0xD, 0x6, 0x8, 0xE, 0x3, + 0x8, 0x3, 0x2, 0x6, 0x4, 0xD, 0xE, 0xB, 0xC, 0x1, 0x7, 0xF, 0xA, 0x0, 0x9, 0x5, + 0x5, 0x2, 0xA, 0xB, 0x9, 0x1, 0xC, 0x3, 0x7, 0x4, 0xD, 0x0, 0x6, 0xF, 0x8, 0xE, + 0x0, 0x4, 0xB, 0xE, 0x8, 0x3, 0x7, 0x1, 0xA, 0x2, 0x9, 0x6, 0xF, 0xD, 0x5, 0xC + ], + 'E-C': [ + 0x1, 0xB, 0xC, 0x2, 0x9, 0xD, 0x0, 0xF, 0x4, 0x5, 0x8, 0xE, 0xA, 0x7, 0x6, 0x3, + 0x0, 0x1, 0x7, 0xD, 0xB, 0x4, 0x5, 0x2, 0x8, 0xE, 0xF, 0xC, 0x9, 0xA, 0x6, 0x3, + 0x8, 0x2, 0x5, 0x0, 0x4, 0x9, 0xF, 0xA, 0x3, 0x7, 0xC, 0xD, 0x6, 0xE, 0x1, 0xB, + 0x3, 0x6, 0x0, 0x1, 0x5, 0xD, 0xA, 0x8, 0xB, 0x2, 0x9, 0x7, 0xE, 0xF, 0xC, 0x4, + 0x8, 0xD, 0xB, 0x0, 0x4, 0x5, 0x1, 0x2, 0x9, 0x3, 0xC, 0xE, 0x6, 0xF, 0xA, 0x7, + 0xC, 0x9, 0xB, 0x1, 0x8, 0xE, 0x2, 0x4, 0x7, 0x3, 0x6, 0x5, 0xA, 0x0, 0xF, 0xD, + 0xA, 0x9, 0x6, 0x8, 0xD, 0xE, 0x2, 0x0, 0xF, 0x3, 0x5, 0xB, 0x4, 0x1, 0xC, 0x7, + 0x7, 0x4, 0x0, 0x5, 0xA, 0x2, 0xF, 0xE, 0xC, 0x6, 0x1, 0xB, 0xD, 0x9, 0x3, 0x8 + ], + 'E-D': [ + 0xF, 0xC, 0x2, 0xA, 0x6, 0x4, 0x5, 0x0, 0x7, 0x9, 0xE, 0xD, 0x1, 0xB, 0x8, 0x3, + 0xB, 0x6, 0x3, 0x4, 0xC, 0xF, 0xE, 0x2, 0x7, 0xD, 0x8, 0x0, 0x5, 0xA, 0x9, 0x1, + 0x1, 0xC, 0xB, 0x0, 0xF, 0xE, 0x6, 0x5, 0xA, 0xD, 0x4, 0x8, 0x9, 0x3, 0x7, 0x2, + 0x1, 0x5, 0xE, 0xC, 0xA, 0x7, 0x0, 0xD, 0x6, 0x2, 0xB, 0x4, 0x9, 0x3, 0xF, 0x8, + 0x0, 0xC, 0x8, 0x9, 0xD, 0x2, 0xA, 0xB, 0x7, 0x3, 0x6, 0x5, 0x4, 0xE, 0xF, 0x1, + 0x8, 0x0, 0xF, 0x3, 0x2, 0x5, 0xE, 0xB, 0x1, 0xA, 0x4, 0x7, 0xC, 0x9, 0xD, 0x6, + 0x3, 0x0, 0x6, 0xF, 0x1, 0xE, 0x9, 0x2, 0xD, 0x8, 0xC, 0x4, 0xB, 0xA, 0x5, 0x7, + 0x1, 0xA, 0x6, 0x8, 0xF, 0xB, 0x0, 0x4, 0xC, 0x3, 0x5, 0x9, 0x7, 0xD, 0x2, 0xE + ], + 'E-SC': [ + 0x3, 0x6, 0x1, 0x0, 0x5, 0x7, 0xd, 0x9, 0x4, 0xb, 0x8, 0xc, 0xe, 0xf, 0x2, 0xa, + 0x7, 0x1, 0x5, 0x2, 0x8, 0xb, 0x9, 0xc, 0xd, 0x0, 0x3, 0xa, 0xf, 0xe, 0x4, 0x6, + 0xf, 0x1, 0x4, 0x6, 0xc, 0x8, 0x9, 0x2, 0xe, 0x3, 0x7, 0xa, 0xb, 0xd, 0x5, 0x0, + 0x3, 0x4, 0xf, 0xc, 0x5, 0x9, 0xe, 0x0, 0x6, 0x8, 0x7, 0xa, 0x1, 0xb, 0xd, 0x2, + 0x6, 0x9, 0x0, 0x7, 0xb, 0x8, 0x4, 0xc, 0x2, 0xe, 0xa, 0xf, 0x1, 0xd, 0x5, 0x3, + 0x6, 0x1, 0x2, 0xf, 0x0, 0xb, 0x9, 0xc, 0x7, 0xd, 0xa, 0x5, 0x8, 0x4, 0xe, 0x3, + 0x0, 0x2, 0xe, 0xc, 0x9, 0x1, 0x4, 0x7, 0x3, 0xf, 0x6, 0x8, 0xa, 0xd, 0xb, 0x5, + 0x5, 0x2, 0xb, 0x8, 0x4, 0xc, 0x7, 0x1, 0xa, 0x6, 0xe, 0x0, 0x9, 0x3, 0xd, 0xf + ], + 'E-Z': [// This is default S-box in according to draft of new standard + 0xc, 0x4, 0x6, 0x2, 0xa, 0x5, 0xb, 0x9, 0xe, 0x8, 0xd, 0x7, 0x0, 0x3, 0xf, 0x1, + 0x6, 0x8, 0x2, 0x3, 0x9, 0xa, 0x5, 0xc, 0x1, 0xe, 0x4, 0x7, 0xb, 0xd, 0x0, 0xf, + 0xb, 0x3, 0x5, 0x8, 0x2, 0xf, 0xa, 0xd, 0xe, 0x1, 0x7, 0x4, 0xc, 0x9, 0x6, 0x0, + 0xc, 0x8, 0x2, 0x1, 0xd, 0x4, 0xf, 0x6, 0x7, 0x0, 0xa, 0x5, 0x3, 0xe, 0x9, 0xb, + 0x7, 0xf, 0x5, 0xa, 0x8, 0x1, 0x6, 0xd, 0x0, 0x9, 0x3, 0xe, 0xb, 0x4, 0x2, 0xc, + 0x5, 0xd, 0xf, 0x6, 0x9, 0x2, 0xc, 0xa, 0xb, 0x7, 0x8, 0x1, 0x4, 0x3, 0xe, 0x0, + 0x8, 0xe, 0x2, 0x5, 0x6, 0x9, 0x1, 0xc, 0xf, 0x4, 0xb, 0x0, 0xd, 0xa, 0x3, 0x7, + 0x1, 0x7, 0xe, 0xd, 0x0, 0x5, 0x8, 0x3, 0x4, 0xf, 0xa, 0x6, 0x9, 0xc, 0xb, 0x2 + ], + //S-box for digest + 'D-TEST': [ + 0x4, 0xA, 0x9, 0x2, 0xD, 0x8, 0x0, 0xE, 0x6, 0xB, 0x1, 0xC, 0x7, 0xF, 0x5, 0x3, + 0xE, 0xB, 0x4, 0xC, 0x6, 0xD, 0xF, 0xA, 0x2, 0x3, 0x8, 0x1, 0x0, 0x7, 0x5, 0x9, + 0x5, 0x8, 0x1, 0xD, 0xA, 0x3, 0x4, 0x2, 0xE, 0xF, 0xC, 0x7, 0x6, 0x0, 0x9, 0xB, + 0x7, 0xD, 0xA, 0x1, 0x0, 0x8, 0x9, 0xF, 0xE, 0x4, 0x6, 0xC, 0xB, 0x2, 0x5, 0x3, + 0x6, 0xC, 0x7, 0x1, 0x5, 0xF, 0xD, 0x8, 0x4, 0xA, 0x9, 0xE, 0x0, 0x3, 0xB, 0x2, + 0x4, 0xB, 0xA, 0x0, 0x7, 0x2, 0x1, 0xD, 0x3, 0x6, 0x8, 0x5, 0x9, 0xC, 0xF, 0xE, + 0xD, 0xB, 0x4, 0x1, 0x3, 0xF, 0x5, 0x9, 0x0, 0xA, 0xE, 0x7, 0x6, 0x8, 0x2, 0xC, + 0x1, 0xF, 0xD, 0x0, 0x5, 0x7, 0xA, 0x4, 0x9, 0x2, 0x3, 0xE, 0x6, 0xB, 0x8, 0xC + ], + 'D-A': [ + 0xA, 0x4, 0x5, 0x6, 0x8, 0x1, 0x3, 0x7, 0xD, 0xC, 0xE, 0x0, 0x9, 0x2, 0xB, 0xF, + 0x5, 0xF, 0x4, 0x0, 0x2, 0xD, 0xB, 0x9, 0x1, 0x7, 0x6, 0x3, 0xC, 0xE, 0xA, 0x8, + 0x7, 0xF, 0xC, 0xE, 0x9, 0x4, 0x1, 0x0, 0x3, 0xB, 0x5, 0x2, 0x6, 0xA, 0x8, 0xD, + 0x4, 0xA, 0x7, 0xC, 0x0, 0xF, 0x2, 0x8, 0xE, 0x1, 0x6, 0x5, 0xD, 0xB, 0x9, 0x3, + 0x7, 0x6, 0x4, 0xB, 0x9, 0xC, 0x2, 0xA, 0x1, 0x8, 0x0, 0xE, 0xF, 0xD, 0x3, 0x5, + 0x7, 0x6, 0x2, 0x4, 0xD, 0x9, 0xF, 0x0, 0xA, 0x1, 0x5, 0xB, 0x8, 0xE, 0xC, 0x3, + 0xD, 0xE, 0x4, 0x1, 0x7, 0x0, 0x5, 0xA, 0x3, 0xC, 0x8, 0xF, 0x6, 0x2, 0x9, 0xB, + 0x1, 0x3, 0xA, 0x9, 0x5, 0xB, 0x4, 0xF, 0x8, 0x6, 0x7, 0xE, 0xD, 0x0, 0x2, 0xC + ], + 'D-SC': [ + 0xb, 0xd, 0x7, 0x0, 0x5, 0x4, 0x1, 0xf, 0x9, 0xe, 0x6, 0xa, 0x3, 0xc, 0x8, 0x2, + 0x1, 0x2, 0x7, 0x9, 0xd, 0xb, 0xf, 0x8, 0xe, 0xc, 0x4, 0x0, 0x5, 0x6, 0xa, 0x3, + 0x5, 0x1, 0xd, 0x3, 0xf, 0x6, 0xc, 0x7, 0x9, 0x8, 0xb, 0x2, 0x4, 0xe, 0x0, 0xa, + 0xd, 0x1, 0xb, 0x4, 0x9, 0xc, 0xe, 0x0, 0x7, 0x5, 0x8, 0xf, 0x6, 0x2, 0xa, 0x3, + 0x2, 0xd, 0xa, 0xf, 0x9, 0xb, 0x3, 0x7, 0x8, 0xc, 0x5, 0xe, 0x6, 0x0, 0x1, 0x4, + 0x0, 0x4, 0x6, 0xc, 0x5, 0x3, 0x8, 0xd, 0xa, 0xb, 0xf, 0x2, 0x1, 0x9, 0x7, 0xe, + 0x1, 0x3, 0xc, 0x8, 0xa, 0x6, 0xb, 0x0, 0x2, 0xe, 0x7, 0x9, 0xf, 0x4, 0x5, 0xd, + 0xa, 0xb, 0x6, 0x0, 0x1, 0x3, 0x4, 0x7, 0xe, 0xd, 0x5, 0xf, 0x8, 0x2, 0x9, 0xc + ] +}; + +var C = new Uint8Array([ + 0x69, 0x00, 0x72, 0x22, 0x64, 0xC9, 0x04, 0x23, + 0x8D, 0x3A, 0xDB, 0x96, 0x46, 0xE9, 0x2A, 0xC4, + 0x18, 0xFE, 0xAC, 0x94, 0x00, 0xED, 0x07, 0x12, + 0xC0, 0x86, 0xDC, 0xC2, 0xEF, 0x4C, 0xA9, 0x2B +]); + +function signed(x) { + return x >= 0x80000000 ? x - 0x100000000 : x; +} + +function unsigned(x) { + return x < 0 ? x + 0x100000000 : x; +} + +// Set random values into Uint8Arry +// Random generator +function randomSeed(e) { + GostRandom = GostRandom || root.GostRandom; + var randomSource = GostRandom ? new (GostRandom || root.GostRandom) : rootCrypto; + if (randomSource.getRandomValues) + randomSource.getRandomValues(e); + else + throw new NotSupportedError('Random generator not found'); +} + +// Get buffer +function buffer(d) { + if (d instanceof CryptoOperationData) + return d; + else if (d && d.buffer && d.buffer instanceof CryptoOperationData) + return d.byteOffset === 0 && d.byteLength === d.buffer.byteLength ? + d.buffer : new Uint8Array(new Uint8Array(d, d.byteOffset, d.byteLength)).buffer; + else + throw new DataError('CryptoOperationData required'); +} + +// Get byte array +function byteArray(d) { + return new Uint8Array(buffer(d)); +} + +// Clone byte array +function cloneArray(d) { + return new Uint8Array(byteArray(d)); +} + + +// Get int32 array +function intArray(d) { + return new Int32Array(buffer(d)); +} + +// Swap bytes for version 2015 +function swap32(b) { + return ((b & 0xff) << 24) + | ((b & 0xff00) << 8) + | ((b >> 8) & 0xff00) + | ((b >> 24) & 0xff); +} + +// + +/* + * Initial parameters and common algortithms of GOST R 34.12-15 + * Algorithm "Kuznechik" 128bit + * + */ // + +// Default initial vector +var defaultIV128 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + +// Mult table for R function +var multTable = (function () { + + // Multiply two numbers in the GF(2^8) finite field defined + // by the polynomial x^8 + x^7 + x^6 + x + 1 = 0 */ + function gmul(a, b) { + var p = 0, counter, carry; + for (counter = 0; counter < 8; counter++) { + if (b & 1) + p ^= a; + carry = a & 0x80; // detect if x^8 term is about to be generated + a = (a << 1) & 0xff; + if (carry) + a ^= 0xc3; // replace x^8 with x^7 + x^6 + x + 1 + b >>= 1; + } + return p & 0xff; + } + + // It is required only this values for R function + // 0 1 2 3 4 5 6 7 + var x = [1, 16, 32, 133, 148, 192, 194, 251]; + var m = []; + for (var i = 0; i < 8; i++) { + m[i] = []; + for (var j = 0; j < 256; j++) + m[i][j] = gmul(x[i], j); + } + return m; +})(); + +// 148, 32, 133, 16, 194, 192, 1, 251, 1, 192, 194, 16, 133, 32, 148, 1 +var kB = [4, 2, 3, 1, 6, 5, 0, 7, 0, 5, 6, 1, 3, 2, 4, 0]; + +// R - function +function funcR(d) { + var sum = 0; + for (var i = 0; i < 16; i++) + sum ^= multTable[kB[i]][d[i]]; + + for (var i = 16; i > 0; --i) + d[i] = d[i - 1]; + d[0] = sum; +} + +function funcReverseR(d) { + var tmp = d[0]; + for (var i = 0; i < 15; i++) + d[i] = d[i + 1]; + d[15] = tmp; + + var sum = 0; + for (i = 0; i < 16; i++) + sum ^= multTable[kB[i]][d[i]]; + d[15] = sum; +} + +// Nonlinear transformation +var kPi = [ + 252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, 218, 35, 197, 4, 77, + 233, 119, 240, 219, 147, 46, 153, 186, 23, 54, 241, 187, 20, 205, 95, 193, + 249, 24, 101, 90, 226, 92, 239, 33, 129, 28, 60, 66, 139, 1, 142, 79, + 5, 132, 2, 174, 227, 106, 143, 160, 6, 11, 237, 152, 127, 212, 211, 31, + 235, 52, 44, 81, 234, 200, 72, 171, 242, 42, 104, 162, 253, 58, 206, 204, + 181, 112, 14, 86, 8, 12, 118, 18, 191, 114, 19, 71, 156, 183, 93, 135, + 21, 161, 150, 41, 16, 123, 154, 199, 243, 145, 120, 111, 157, 158, 178, 177, + 50, 117, 25, 61, 255, 53, 138, 126, 109, 84, 198, 128, 195, 189, 13, 87, + 223, 245, 36, 169, 62, 168, 67, 201, 215, 121, 214, 246, 124, 34, 185, 3, + 224, 15, 236, 222, 122, 148, 176, 188, 220, 232, 40, 80, 78, 51, 10, 74, + 167, 151, 96, 115, 30, 0, 98, 68, 26, 184, 56, 130, 100, 159, 38, 65, + 173, 69, 70, 146, 39, 94, 85, 47, 140, 163, 165, 125, 105, 213, 149, 59, + 7, 88, 179, 64, 134, 172, 29, 247, 48, 55, 107, 228, 136, 217, 231, 137, + 225, 27, 131, 73, 76, 63, 248, 254, 141, 83, 170, 144, 202, 216, 133, 97, + 32, 113, 103, 164, 45, 43, 9, 91, 203, 155, 37, 208, 190, 229, 108, 82, + 89, 166, 116, 210, 230, 244, 180, 192, 209, 102, 175, 194, 57, 75, 99, 182 +]; + +var kReversePi = (function () { + var m = []; + for (var i = 0, n = kPi.length; i < n; i++) + m[kPi[i]] = i; + return m; +})(); + +function funcS(d) { + for (var i = 0; i < 16; ++i) + d[i] = kPi[d[i]]; +} + +function funcReverseS(d) { + for (var i = 0; i < 16; ++i) + d[i] = kReversePi[d[i]]; +} + +function funcX(a, b) { + for (var i = 0; i < 16; ++i) + a[i] ^= b[i]; +} + +function funcL(d) { + for (var i = 0; i < 16; ++i) + funcR(d); +} + +function funcReverseL(d) { + for (var i = 0; i < 16; ++i) + funcReverseR(d); +} + +function funcLSX(a, b) { + funcX(a, b); + funcS(a); + funcL(a); +} + +function funcReverseLSX(a, b) { + funcX(a, b); + funcReverseL(a); + funcReverseS(a); +} + +function funcF(inputKey, inputKeySecond, iterationConst) { + var tmp = new Uint8Array(inputKey); + funcLSX(inputKey, iterationConst); + funcX(inputKey, inputKeySecond); + inputKeySecond.set(tmp); +} + +function funcC(number, d) { + for (var i = 0; i < 15; i++) + d[i] = 0; + d[15] = number; + funcL(d); +} + +// + +/** + * Key schedule for GOST R 34.12-15 128bits + * + * @memberOf GostCipher + * @private + * @instance + * @method keySchedule + * @param {type} k + * @returns {Uint8Array} + */ +function keySchedule128(k) // +{ + var keys = new Uint8Array(160), c = new Uint8Array(16); + keys.set(byteArray(k)); + for (var j = 0; j < 4; j++) { + var j0 = 32 * j, j1 = 32 * (j + 1); + keys.set(new Uint8Array(keys.buffer, j0, 32), j1); + for (var i = 1; i < 9; i++) { + funcC(j * 8 + i, c); + funcF(new Uint8Array(keys.buffer, j1, 16), + new Uint8Array(keys.buffer, j1 + 16, 16), c); + } + } + return keys; +} // + +/** + * GOST R 34.12-15 128 bits encrypt/decrypt process + * + * @memberOf GostCipher + * @private + * @instance + * @method round + * @param {Uint8Array} k Scheduled key + * @param {Uint8Array} d Data + * @param {number} ofs Offsec + * @param {number} e true - decrypt + */ +function process128(k, d, ofs, e) // +{ + ofs = ofs || d.byteOffset; + var r = new Uint8Array(d.buffer, ofs, 16); + if (e) { + for (var i = 0; i < 9; i++) + funcReverseLSX(r, new Uint8Array(k.buffer, (9 - i) * 16, 16)); + + funcX(r, new Uint8Array(k.buffer, 0, 16)); + } else { + for (var i = 0; i < 9; i++) + funcLSX(r, new Uint8Array(k.buffer, 16 * i, 16)); + + funcX(r, new Uint8Array(k.buffer, 16 * 9, 16)); + } +} // + +/** + * One GOST encryption round + * + * @memberOf GostCipher + * @private + * @instance + * @method round + * @param {Int8Array} S sBox + * @param {Int32Array} m 2x32 bits cipher block + * @param {Int32Array} k 32 bits key[i] + */ +function round(S, m, k) // +{ + var cm = (m[0] + k) & 0xffffffff; + + var om = S[ 0 + ((cm >> (0 * 4)) & 0xF)] << (0 * 4); + om |= S[ 16 + ((cm >> (1 * 4)) & 0xF)] << (1 * 4); + om |= S[ 32 + ((cm >> (2 * 4)) & 0xF)] << (2 * 4); + om |= S[ 48 + ((cm >> (3 * 4)) & 0xF)] << (3 * 4); + om |= S[ 64 + ((cm >> (4 * 4)) & 0xF)] << (4 * 4); + om |= S[ 80 + ((cm >> (5 * 4)) & 0xF)] << (5 * 4); + om |= S[ 96 + ((cm >> (6 * 4)) & 0xF)] << (6 * 4); + om |= S[112 + ((cm >> (7 * 4)) & 0xF)] << (7 * 4); + cm = om << 11 | om >>> (32 - 11); + + cm ^= m[1]; + m[1] = m[0]; + m[0] = cm; + +} // + +/** + * Process encrypt/decrypt block with key K using GOST 28147-89 + * + * @memberOf GostCipher + * @private + * @instance + * @method process + * @param k {Int32Array} 8x32 bits key + * @param d {Int32Array} 8x8 bits cipher block + * @param ofs {number} offset + */ +function process89(k, d, ofs) // +{ + ofs = ofs || d.byteOffset; + var s = this.sBox, + m = new Int32Array(d.buffer, ofs, 2); + + for (var i = 0; i < 32; i++) + round(s, m, k[i]); + + var r = m[0]; + m[0] = m[1]; + m[1] = r; +} // + +/** + * Process encrypt/decrypt block with key K using GOST R 34.12-15 64bit block + * + * @memberOf GostCipher + * @private + * @instance + * @method process + * @param k {Int32Array} 8x32 bits key + * @param d {Int32Array} 8x8 bits cipher block + * @param ofs {number} offset + */ +function process15(k, d, ofs) // +{ + ofs = ofs || d.byteOffset; + var s = this.sBox, + m = new Int32Array(d.buffer, ofs, 2), + r = swap32(m[0]); + m[0] = swap32(m[1]); + m[1] = r; + + for (var i = 0; i < 32; i++) + round(s, m, k[i]); + + m[0] = swap32(m[0]); + m[1] = swap32(m[1]); +} // + +/** + * Key keySchedule algorithm for GOST 28147-89 64bit cipher + * + * @memberOf GostCipher + * @private + * @instance + * @method process + * @param k {Uint8Array} 8 bit key array + * @param e {boolean} true - decrypt + * @returns {Int32Array} keyScheduled 32-bit key + */ +function keySchedule89(k, e) // +{ + var sch = new Int32Array(32), + key = new Int32Array(buffer(k)); + + for (var i = 0; i < 8; i++) + sch[i] = key[i]; + + if (e) { + for (var i = 0; i < 8; i++) + sch[i + 8] = sch[7 - i]; + + for (var i = 0; i < 8; i++) + sch[i + 16] = sch[7 - i]; + } else { + for (var i = 0; i < 8; i++) + sch[i + 8] = sch[i]; + + for (var i = 0; i < 8; i++) + sch[i + 16] = sch[i]; + } + + for (var i = 0; i < 8; i++) + sch[i + 24] = sch[7 - i]; + + return sch; +} // + +/** + * Key keySchedule algorithm for GOST R 34.12-15 64bit cipher + * + * @memberOf GostCipher + * @private + * @instance + * @method process + * @param k {Uint8Array} 8 bit key array + * @param e {boolean} true - decrypt + * @returns {Int32Array} keyScheduled 32-bit key + */ +function keySchedule15(k, e) // +{ + var sch = new Int32Array(32), + key = new Int32Array(buffer(k)); + + for (var i = 0; i < 8; i++) + sch[i] = swap32(key[i]); + + if (e) { + for (var i = 0; i < 8; i++) + sch[i + 8] = sch[7 - i]; + + for (var i = 0; i < 8; i++) + sch[i + 16] = sch[7 - i]; + } else { + for (var i = 0; i < 8; i++) + sch[i + 8] = sch[i]; + + for (var i = 0; i < 8; i++) + sch[i + 16] = sch[i]; + } + + for (var i = 0; i < 8; i++) + sch[i + 24] = sch[7 - i]; + + return sch; +} // + +/** + * Key schedule for RC2 + * + * https://tools.ietf.org/html/rfc2268 + * + * @memberOf GostCipher + * @private + * @instance + * @method keySchedule + * @param {Uint8Array} k + * @returns {Uint16Array} + */ +var keyScheduleRC2 = (function () // +{ + // an array of "random" bytes based on the digits of PI = 3.14159... + var PITABLE = new Uint8Array([ + 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, + 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, + 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32, + 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82, + 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, + 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, + 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03, + 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, + 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, + 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec, + 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, + 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, + 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9, + 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, + 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, + 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad + ]); + + return function (k) + { + var key = new Uint8Array(buffer(k)), + T = Math.min(key.length, 128), + T1 = this.effectiveLength, + T8 = Math.floor((T1 + 7) / 8), + TM = 0xff % Math.pow(2, 8 + T1 - 8 * T8); + + var L = new Uint8Array(128), K = new Uint16Array(L.buffer); + for (var i = 0; i < T; i++) + L[i] = key[i]; + for (var i = T; i < 128; i++) + L[i] = PITABLE[(L[i - 1] + L[i - T]) % 256]; + L[128 - T8] = PITABLE[L[128 - T8] & TM]; + for (var i = 127 - T8; i >= 0; --i) + L[i] = PITABLE[L[i + 1] ^ L[i + T8]]; + return K; + }; +} // +)(); + +/** + * RC2 encrypt/decrypt process + * + * https://tools.ietf.org/html/rfc2268 + * + * @memberOf GostCipher + * @private + * @instance + * @method round + * @param {CryptoOperationData} k Scheduled key + * @param {CryptoOperationData} d Data + * @param {number} ofs Offsec + * @param {number} e true - decrypt + */ +var processRC2 = (function () // +{ + var K, j, R = new Uint16Array(4), + s = new Uint16Array([1, 2, 3, 5]), reverse; + + function rol(R, s) { + return (R << s | R >>> (16 - s)) & 0xffff; + } + + function ror(R, s) { + return (R >>> s | R << (16 - s)) & 0xffff; + } + + function mix(i) { + if (reverse) { + R[i] = ror(R[i], s[i]); + R[i] = R[i] - K[j] - (R[(i + 3) % 4] & R[(i + 2) % 4]) - ((~R[(i + 3) % 4]) & R[(i + 1) % 4]); + j = j - 1; + } else { + R[i] = R[i] + K[j] + (R[(i + 3) % 4] & R[(i + 2) % 4]) + ((~R[(i + 3) % 4]) & R[(i + 1) % 4]); + j = j + 1; + R[i] = rol(R[i], s[i]); + } + } + + function mash(i) { + if (reverse) { + R[i] = R[i] - K[R[(i + 3) % 4] & 63]; + } else { + R[i] = R[i] + K[R[(i + 3) % 4] & 63]; + } + } + + function perform(method, count) { + count = count || 1; + for (var j = 0; j < count; j++) { + if (reverse) { + for (var i = 3; i >= 0; --i) + method(i); + } else { + for (var i = 0; i < 4; i++) + method(i); + } + } + } + + return function (k, d, ofs, e) { + reverse = e; + // 1. Initialize words R[0], ..., R[3] to contain the 64-bit + // ciphertext value. + R = new Uint16Array(d.buffer, ofs || d.byteOffset, 4); + // 2. Expand the key, so that words K[0], ..., K[63] become + // defined. + K = k; + // 3. Initialize j to zero (enc) j to 63 (dec). + j = e ? 63 : 0; + // 4. Perform five mixing rounds. + perform(mix, 5); + // 5. Perform one mashing round. + perform(mash); + // 6. Perform six mixing rounds. + perform(mix, 6); + // 7. Perform one mashing round. + perform(mash); + // 8. Perform five mixing rounds. + perform(mix, 5); + }; +} // +)(); + +/** + * Algorithm name GOST 28147-ECB

+ * + * encryptECB (K, D) is D, encrypted with key k using GOST 28147/GOST R 34.13 in + * "prostaya zamena" (Electronic Codebook, ECB) mode. + * @memberOf GostCipher + * @method encrypt + * @instance + * @param k {CryptoOperationData} 8x32 bit key + * @param d {CryptoOperationData} 8 bits message + * @return {CryptoOperationData} result + */ +function encryptECB(k, d) // +{ + var p = this.pad(byteArray(d)), + n = this.blockSize, + b = p.byteLength / n, + key = this.keySchedule(k); + + for (var i = 0; i < b; i++) + this.process(key, p, n * i); + + return p.buffer; +} // + +/** + * Algorithm name GOST 28147-ECB

+ * + * decryptECB (K, D) is D, decrypted with key K using GOST 28147/GOST R 34.13 in + * "prostaya zamena" (Electronic Codebook, ECB) mode. + * + * @memberOf GostCipher + * @method decrypt + * @instance + * @param k {CryptoOperationData} 8x32 bits key + * @param d {CryptoOperationData} 8 bits message + * @return {CryptoOperationData} result + */ +function decryptECB(k, d) // +{ + var p = cloneArray(d), + n = this.blockSize, + b = p.byteLength / n, + key = this.keySchedule(k, 1); + + for (var i = 0; i < b; i++) + this.process(key, p, n * i, 1); + + return this.unpad(p).buffer; +} // + +/** + * Algorithm name GOST 28147-CFB

+ * + * encryptCFB (IV, K, D) is D, encrypted with key K using GOST 28147/GOST R 34.13 + * in "gammirovanie s obratnoj svyaziyu" (Cipher Feedback, CFB) mode, and IV is + * used as the initialization vector. + * + * @memberOf GostCipher + * @method encrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ +function encryptCFB(k, d, iv) // +{ + var s = new Uint8Array(iv || this.iv), + c = cloneArray(d), + m = s.length, + t = new Uint8Array(m), + b = this.shiftBits >> 3, + cb = c.length, r = cb % b, q = (cb - r) / b, + key = this.keySchedule(k); + + for (var i = 0; i < q; i++) { + + for (var j = 0; j < m; j++) + t[j] = s[j]; + + this.process(key, s); + + for (var j = 0; j < b; j++) + c[i * b + j] ^= s[j]; + + for (var j = 0; j < m - b; j++) + s[j] = t[b + j]; + + for (var j = 0; j < b; j++) + s[m - b + j] = c[i * b + j]; + + k = this.keyMeshing(k, s, i, key); + } + + if (r > 0) { + this.process(key, s); + + for (var i = 0; i < r; i++) + c[q * b + i] ^= s[i]; + } + return c.buffer; +} // + +/** + * Algorithm name GOST 28147-CFB

+ * + * decryptCFB (IV, K, D) is D, decrypted with key K using GOST 28147/GOST R 34.13 + * in "gammirovanie s obratnoj svyaziyu po shifrotekstu" (Cipher Feedback, CFB) mode, and IV is + * used as the initialization vector. + * + * @memberOf GostCipher + * @method decrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ +function decryptCFB(k, d, iv) // +{ + var s = new Uint8Array(iv || this.iv), + c = cloneArray(d), + m = s.length, + t = new Uint8Array(m), + b = this.shiftBits >> 3, + cb = c.length, r = cb % b, q = (cb - r) / b, + key = this.keySchedule(k); + + for (var i = 0; i < q; i++) { + + for (var j = 0; j < m; j++) + t[j] = s[j]; + + this.process(key, s); + + for (var j = 0; j < b; j++) { + t[j] = c[i * b + j]; + c[i * b + j] ^= s[j]; + } + + for (var j = 0; j < m - b; j++) + s[j] = t[b + j]; + + for (var j = 0; j < b; j++) + s[m - b + j] = t[j]; + + k = this.keyMeshing(k, s, i, key); + } + + if (r > 0) { + this.process(key, s); + + for (var i = 0; i < r; i++) + c[q * b + i] ^= s[i]; + } + return c.buffer; +} // + +/** + * Algorithm name GOST 28147-OFB

+ * + * encryptOFB/decryptOFB (IV, K, D) is D, encrypted with key K using GOST 28147/GOST R 34.13 + * in "gammirovanie s obratnoj svyaziyu po vyhodu" (Output Feedback, OFB) mode, and IV is + * used as the initialization vector. + * + * @memberOf GostCipher + * @method encrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv 8x8 optional bits initial vector + * @return {CryptoOperationData} result + */ +/** + * Algorithm name GOST 28147-OFB

+ * + * encryptOFB/decryptOFB (IV, K, D) is D, encrypted with key K using GOST 28147/GOST R 34.13 + * in "gammirovanie s obratnoj svyaziyu po vyhodu" (Output Feedback, OFB) mode, and IV is + * used as the initialization vector. + * + * @memberOf GostCipher + * @method decrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ +function processOFB(k, d, iv) // +{ + var s = new Uint8Array(iv || this.iv), + c = cloneArray(d), + m = s.length, + t = new Uint8Array(m), + b = this.shiftBits >> 3, + p = new Uint8Array(b), + cb = c.length, r = cb % b, q = (cb - r) / b, + key = this.keySchedule(k); + + for (var i = 0; i < q; i++) { + + for (var j = 0; j < m; j++) + t[j] = s[j]; + + this.process(key, s); + + for (var j = 0; j < b; j++) + p[j] = s[j]; + + for (var j = 0; j < b; j++) + c[i * b + j] ^= s[j]; + + for (var j = 0; j < m - b; j++) + s[j] = t[b + j]; + + for (var j = 0; j < b; j++) + s[m - b + j] = p[j]; + + k = this.keyMeshing(k, s, i, key); + } + + if (r > 0) { + this.process(key, s); + + for (var i = 0; i < r; i++) + c[q * b + i] ^= s[i]; + } + return c.buffer; +} // + +/** + * Algorithm name GOST 28147-CTR

+ * + * encryptCTR/decryptCTR (IV, K, D) is D, encrypted with key K using GOST 28147/GOST R 34.13 + * in "gammirovanie" (Counter Mode-CTR) mode, and IV is used as the + * initialization vector. + * @memberOf GostCipher + * @method encrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv 8x8 optional bits initial vector + * @return {CryptoOperationData} result + */ +/** + * Algorithm name GOST 28147-CTR

+ * + * encryptCTR/decryptCTR (IV, K, D) is D, encrypted with key K using GOST 28147/GOST R 34.13 + * in "gammirovanie" (Counter Mode-CTR) mode, and IV is used as the + * initialization vector. + * @memberOf GostCipher + * @method decrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ +function processCTR89(k, d, iv) // +{ + var s = new Uint8Array(iv || this.iv), + c = cloneArray(d), + b = this.blockSize, + t = new Int8Array(b), + cb = c.length, r = cb % b, q = (cb - r) / b, + key = this.keySchedule(k), + syn = new Int32Array(s.buffer); + + this.process(key, s); + + for (var i = 0; i < q; i++) { + syn[0] = (syn[0] + 0x1010101) & 0xffffffff; + // syn[1] = signed(unsigned((syn[1] + 0x1010104) & 0xffffffff) % 0xffffffff); + var tmp = unsigned(syn[1]) + 0x1010104; // Special thanks to Ilya Matveychikov + syn[1] = signed(tmp < 0x100000000 ? tmp : tmp - 0xffffffff); + + for (var j = 0; j < b; j++) + t[j] = s[j]; + + this.process(key, syn); + + for (var j = 0; j < b; j++) + c[i * b + j] ^= s[j]; + + for (var j = 0; j < b; j++) + s[j] = t[j]; + + k = this.keyMeshing(k, s, i, key); + } + if (r > 0) { + syn[0] = (syn[0] + 0x1010101) & 0xffffffff; + // syn[1] = signed(unsigned((syn[1] + 0x1010104) & 0xffffffff) % 0xffffffff); + var tmp = unsigned(syn[1]) + 0x1010104; // Special thanks to Ilya Matveychikov + syn[1] = signed(tmp < 0x100000000 ? tmp : tmp - 0xffffffff); + + this.process(key, syn); + + for (var i = 0; i < r; i++) + c[q * b + i] ^= s[i]; + } + return c.buffer; +} // + +function processCTR15(k, d, iv) // +{ + var c = cloneArray(d), + n = this.blockSize, + b = this.shiftBits >> 3, + cb = c.length, r = cb % b, q = (cb - r) / b, + s = new Uint8Array(n), + t = new Int32Array(n), + key = this.keySchedule(k); + + s.set(iv || this.iv); + for (var i = 0; i < q; i++) { + + for (var j = 0; j < n; j++) + t[j] = s[j]; + + this.process(key, s); + + for (var j = 0; j < b; j++) + c[b * i + j] ^= s[j]; + + for (var j = 0; j < n; j++) + s[j] = t[j]; + + for (var j = n - 1; i >= 0; --i) { + if (s[j] > 0xfe) { + s[j] -= 0xfe; + } else { + s[j]++; + break; + } + } + } + + if (r > 0) { + this.process(key, s); + for (var j = 0; j < r; j++) + c[b * q + j] ^= s[j]; + } + + return c.buffer; +} // + +/** + * Algorithm name GOST 28147-CBC

+ * + * encryptCBC (IV, K, D) is D, encrypted with key K using GOST 28147/GOST R 34.13 + * in "Prostaya zamena s zatsepleniem" (Cipher-Block-Chaining, CBC) mode and IV is used as the initialization + * vector. + * + * @memberOf GostCipher + * @method encrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ +function encryptCBC(k, d, iv) // +{ + var s = new Uint8Array(iv || this.iv), + n = this.blockSize, + m = s.length, + c = this.pad(byteArray(d)), + key = this.keySchedule(k); + + for (var i = 0, b = c.length / n; i < b; i++) { + + for (var j = 0; j < n; j++) + s[j] ^= c[i * n + j]; + + this.process(key, s); + + for (var j = 0; j < n; j++) + c[i * n + j] = s[j]; + + if (m !== n) { + for (var j = 0; j < m - n; j++) + s[j] = s[n + j]; + + for (var j = 0; j < n; j++) + s[j + m - n] = c[i * n + j]; + } + + k = this.keyMeshing(k, s, i, key); + } + + return c.buffer; +} // + +/** + * Algorithm name GOST 28147-CBC

+ * + * decryptCBC (IV, K, D) is D, decrypted with key K using GOST 28147/GOST R 34.13 + * in "Prostaya zamena s zatsepleniem" (Cipher-Block-Chaining, CBC) mode and IV is used as the initialization + * vector. + * + * @memberOf GostCipher + * @method decrypt + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ +function decryptCBC(k, d, iv) // +{ + var s = new Uint8Array(iv || this.iv), + n = this.blockSize, + m = s.length, + c = cloneArray(d), + next = new Uint8Array(n), + key = this.keySchedule(k, 1); + + for (var i = 0, b = c.length / n; i < b; i++) { + + for (var j = 0; j < n; j++) + next[j] = c[i * n + j]; + + this.process(key, c, i * n, 1); + + for (var j = 0; j < n; j++) + c[i * n + j] ^= s[j]; + + if (m !== n) { + for (var j = 0; j < m - n; j++) + s[j] = s[n + j]; + } + + for (var j = 0; j < n; j++) + s[j + m - n] = next[j]; + + k = this.keyMeshing(k, s, i, key, 1); + } + + return this.unpad(c).buffer; +} // + +/** + * The generateKey method returns a new generated key. + * + * @memberOf GostCipher + * @method generateKey + * @instance + * @return {CryptoOperationData} result + */ + +function generateKey() // +{ + // Simple generate 256 bit random seed + var k = new Uint8Array(this.keySize); + randomSeed(k); + return k.buffer; +} // + + +/** + * makeIMIT (K, D) is the 32-bit result of the GOST 28147/GOST R 34.13 in + * "imitovstavka" (MAC) mode, used with D as plaintext, K as key and IV + * as initialization vector. Note that the standard specifies its use + * in this mode only with an initialization vector of zero. + * + * @memberOf GostCipher + * @method processMAC + * @private + * @instance + * @param {Int32Array} key 8x32 bits key + * @param {Int32Array} s 8x8 sum array + * @param {Uint8Array} d 8 bits array with data + * @return {Uint8Array} result + */ +function processMAC89(key, s, d) // +{ + var c = zeroPad.call(this, byteArray(d)), + n = this.blockSize, + q = c.length / n, + sBox = this.sBox, + sum = new Int32Array(s.buffer); + + for (var i = 0; i < q; i++) { + + for (var j = 0; j < n; j++) + s[j] ^= c[i * n + j]; + + for (var j = 0; j < 16; j++) // 1-16 steps + round(sBox, sum, key[j]); + } +} // + +function processKeyMAC15(s) // +{ + var t = 0, n = s.length; + for (var i = n - 1; i >= 0; --i) { + var t1 = s[i] >>> 7; + s[i] = (s[i] << 1) & 0xff | t; + t = t1; + } + if (t !== 0) { + if (n === 16) + s[15] ^= 0x87; + else + s[7] ^= 0x1b; + } +} // + +function processMAC15(key, s, d) // +{ + var n = this.blockSize, + sBox = this.sBox, c = byteArray(d), + r = new Uint8Array(n); + // R + this.process(key, r); + // K1 + processKeyMAC15(r); + if (d.byteLength % n !== 0) { + c = bitPad.call(this, byteArray(d)); + // K2 + processKeyMAC15(r); + } + + for (var i = 0, q = c.length / n; i < q; i++) { + + for (var j = 0; j < n; j++) + s[j] ^= c[i * n + j]; + + if (i === q - 1) {// Last block + for (var j = 0; j < n; j++) + s[j] ^= r[j]; + } + + this.process(key, s); + } +} // + +/** + * signMAC (K, D, IV) is the 32-bit result of the GOST 28147/GOST R 34.13 in + * "imitovstavka" (MAC) mode, used with D as plaintext, K as key and IV + * as initialization vector. Note that the standard specifies its use + * in this mode only with an initialization vector of zero. + * + * @memberOf GostCipher + * @method sign + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv initial vector + * @return {CryptoOperationData} result + */ +function signMAC(k, d, iv) // +{ + var key = this.keySchedule(k), + s = new Uint8Array(iv || this.iv), + m = Math.ceil(this.macLength >> 3) || this.blockSize >> 1; + + this.processMAC(key, s, d); + + var mac = new Uint8Array(m); // mac size + mac.set(new Uint8Array(s.buffer, 0, m)); + return mac.buffer; +} // + +/** + * verifyMAC (K, M, D, IV) the 32-bit result verification of the GOST 28147/GOST R 34.13 in + * "imitovstavka" (MAC) mode, used with D as plaintext, K as key and IV + * as initialization vector. Note that the standard specifies its use + * in this mode only with an initialization vector of zero. + * + * @memberOf GostCipher + * @method verify + * @instance + * @param {CryptoOperationData} k 8x32 bits key + * @param {CryptoOperationData} m 8 bits array with signature + * @param {CryptoOperationData} d 8 bits array with data + * @param {CryptoOperationData} iv 8x8 optional bits initial vector + * @return {boolen} MAC verified = true + */ +function verifyMAC(k, m, d, iv) // +{ + var mac = new Uint8Array(signMAC.call(this, k, d, iv)), + test = byteArray(m); + if (mac.length !== test.length) + return false; + for (var i = 0, n = mac.length; i < n; i++) + if (mac[i] !== test[i]) + return false; + return true; +} // + +/** + * Algorithm name GOST 28147-KW

+ * + * This algorithm encrypts GOST 28147-89 CEK with a GOST 28147/GOST R 34.13 KEK. + * Ref. rfc4357 6.1 GOST 28147-89 Key Wrap + * Note: This algorithm MUST NOT be used with a KEK produced by VKO GOST + * R 34.10-94, because such a KEK is constant for every sender-recipient + * pair. Encrypting many different content encryption keys on the same + * constant KEK may reveal that KEK. + * + * @memberOf GostCipher + * @method wrapKey + * @instance + * @param {CryptoOperationData} kek Key encryption key + * @param {CryptoOperationData} cek Content encryption key + * @returns {CryptoOperationData} Encrypted cek + */ +function wrapKeyGOST(kek, cek) // +{ + var n = this.blockSize, k = this.keySize, len = k + (n >> 1); + // 1) For a unique symmetric KEK, generate 8 octets at random and call + // the result UKM. For a KEK, produced by VKO GOST R 34.10-2001, use + // the UKM that was used for key derivation. + if (!this.ukm) + throw new DataError('UKM must be defined'); + var ukm = new Uint8Array(this.ukm); + // 2) Compute a 4-byte checksum value, GOST 28147IMIT (UKM, KEK, CEK). + // Call the result CEK_MAC. + var mac = signMAC.call(this, kek, cek, ukm); + // 3) Encrypt the CEK in ECB mode using the KEK. Call the ciphertext CEK_ENC. + var enc = encryptECB.call(this, kek, cek); + // 4) The wrapped content-encryption key is (UKM | CEK_ENC | CEK_MAC). + var r = new Uint8Array(len); + r.set(new Uint8Array(enc), 0); + r.set(new Uint8Array(mac), k); + return r.buffer; +} // + +/** + * Algorithm name GOST 28147-KW

+ * + * This algorithm decrypts GOST 28147-89 CEK with a GOST 28147 KEK. + * Ref. rfc4357 6.2 GOST 28147-89 Key Unwrap + * + * @memberOf GostCipher + * @method unwrapKey + * @instance + * @param {type} kek Key encryption key + * @param {type} data Content encryption key + * @return {CryptoOperationData} result + */ +function unwrapKeyGOST(kek, data) // +{ + var n = this.blockSize, k = this.keySize, len = k + (n >> 1); + // 1) If the wrapped content-encryption key is not 44 octets, then error. + var d = buffer(data); + if (d.byteLength !== len) + throw new DataError('Wrapping key size must be ' + len + ' bytes'); + // 2) Decompose the wrapped content-encryption key into UKM, CEK_ENC, and CEK_MAC. + // UKM is the most significant (first) 8 octets. CEK_ENC is next 32 octets, + // and CEK_MAC is the least significant (last) 4 octets. + if (!this.ukm) + throw new DataError('UKM must be defined'); + var ukm = new Uint8Array(this.ukm), + enc = new Uint8Array(d, 0, k), + mac = new Uint8Array(d, k, n >> 1); + // 3) Decrypt CEK_ENC in ECB mode using the KEK. Call the output CEK. + var cek = decryptECB.call(this, kek, enc); + // 4) Compute a 4-byte checksum value, GOST 28147IMIT (UKM, KEK, CEK), + // compare the result with CEK_MAC. If they are not equal, then error. + var check = verifyMAC.call(this, kek, mac, cek, ukm); + if (!check) + throw new DataError('Error verify MAC of wrapping key'); + return cek; +} // + +/** + * Algorithm name GOST 28147-CPKW

+ * + * Given a random 64-bit UKM and a GOST 28147 key K, this algorithm + * creates a new GOST 28147-89 key K(UKM). + * Ref. rfc4357 6.3 CryptoPro KEK Diversification Algorithm + * + * @memberOf GostCipher + * @method diversify + * @instance + * @private + * @param {CryptoOperationData} kek Key encryption key + * @param {CryptoOperationData} ukm Random generated value + * @returns {CryptoOperationData} Diversified kek + */ +function diversifyKEK(kek, ukm) // +{ + var n = this.blockSize; + + // 1) Let K[0] = K; + var k = intArray(kek); + // 2) UKM is split into components a[i,j]: + // UKM = a[0]|..|a[7] (a[i] - byte, a[i,0]..a[i,7] - it’s bits) + var a = []; + for (var i = 0; i < n; i++) { + a[i] = []; + for (var j = 0; j < 8; j++) { + a[i][j] = (ukm[i] >>> j) & 0x1; + } + } + // 3) Let i be 0. + // 4) K[1]..K[8] are calculated by repeating the following algorithm + // eight times: + for (var i = 0; i < n; i++) { + // A) K[i] is split into components k[i,j]: + // K[i] = k[i,0]|k[i,1]|..|k[i,7] (k[i,j] - 32-bit integer) + // B) Vector S[i] is calculated: + // S[i] = ((a[i,0]*k[i,0] + ... + a[i,7]*k[i,7]) mod 2^32) | + // (((~a[i,0])*k[i,0] + ... + (~a[i,7])*k[i,7]) mod 2^32); + var s = new Int32Array(2); + for (var j = 0; j < 8; j++) { + if (a[i][j]) + s[0] = (s[0] + k[j]) & 0xffffffff; + else + s[1] = (s[1] + k[j]) & 0xffffffff; + } + // C) K[i+1] = encryptCFB (S[i], K[i], K[i]) + var iv = new Uint8Array(s.buffer); + k = new Int32Array(encryptCFB.call(this, k, k, iv)); + // D) i = i + 1 + } + // 5) Let K(UKM) be K[8]. + return k; +} // + +/** + * Algorithm name GOST 28147-CPKW

+ * + * This algorithm encrypts GOST 28147-89 CEK with a GOST 28147 KEK. + * It can be used with any KEK (e.g., produced by VKO GOST R 34.10-94 or + * VKO GOST R 34.10-2001) because a unique UKM is used to diversify the KEK. + * Ref. rfc4357 6.3 CryptoPro Key Wrap + * + * @memberOf GostCipher + * @method wrapKey + * @instance + * @param {CryptoOperationData} kek Key encryption key + * @param {CryptoOperationData} cek Content encryption key + * @returns {CryptoOperationData} Encrypted cek + */ +function wrapKeyCP(kek, cek) // +{ + var n = this.blockSize, k = this.keySize, len = k + (n >> 1); + // 1) For a unique symmetric KEK or a KEK produced by VKO GOST R + // 34.10-94, generate 8 octets at random. Call the result UKM. For + // a KEK, produced by VKO GOST R 34.10-2001, use the UKM that was + // used for key derivation. + if (!this.ukm) + throw new DataError('UKM must be defined'); + var ukm = new Uint8Array(this.ukm); + // 2) Diversify KEK, using the CryptoPro KEK Diversification Algorithm, + // described in Section 6.5. Call the result KEK(UKM). + var dek = diversifyKEK.call(this, kek, ukm); + // 3) Compute a 4-byte checksum value, GOST 28147IMIT (UKM, KEK(UKM), + // CEK). Call the result CEK_MAC. + var mac = signMAC.call(this, dek, cek, ukm); + // 4) Encrypt CEK in ECB mode using KEK(UKM). Call the ciphertext + // CEK_ENC. + var enc = encryptECB.call(this, dek, cek); + // 5) The wrapped content-encryption key is (UKM | CEK_ENC | CEK_MAC). + var r = new Uint8Array(len); + r.set(new Uint8Array(enc), 0); + r.set(new Uint8Array(mac), k); + return r.buffer; +} // + +/** + * Algorithm name GOST 28147-CPKW

+ * + * This algorithm encrypts GOST 28147-89 CEK with a GOST 28147 KEK. + * Ref. rfc4357 6.4 CryptoPro Key Unwrap + * + * @memberOf GostCipher + * @method unwrapKey + * @instance + * @param {CryptoOperationData} kek Key encryption key + * @param {CryptoOperationData} data Encrypted content encryption keu + * @return {CryptoOperationData} result Decrypted content encryption keu + */ +function unwrapKeyCP(kek, data) // +{ + var n = this.blockSize, k = this.keySize, len = k + (n >> 1); + // 1) If the wrapped content-encryption key is not 44 octets, then error. + var d = buffer(data); + if (d.byteLength !== len) + throw new DataError('Wrapping key size must be ' + len + ' bytes'); + // 2) Decompose the wrapped content-encryption key into UKM, CEK_ENC, + // and CEK_MAC. UKM is the most significant (first) 8 octets. + // CEK_ENC is next 32 octets, and CEK_MAC is the least significant + // (last) 4 octets. + if (!this.ukm) + throw new DataError('UKM must be defined'); + var ukm = new Uint8Array(this.ukm), + enc = new Uint8Array(d, 0, k), + mac = new Uint8Array(d, k, n >> 1); + // 3) Diversify KEK using the CryptoPro KEK Diversification Algorithm, + // described in section 6.5. Call the result KEK(UKM). + var dek = diversifyKEK.call(this, kek, ukm); + // 4) Decrypt CEK_ENC in ECB mode using KEK(UKM). Call the output CEK. + var cek = decryptECB.call(this, dek, enc); + // 5) Compute a 4-byte checksum value, GOST 28147IMIT (UKM, KEK(UKM), + // CEK), compare the result with CEK_MAC. If they are not equal, + // then it is an error. + var check = verifyMAC.call(this, dek, mac, cek, ukm); + if (!check) + throw new DataError('Error verify MAC of wrapping key'); + return cek; +} // + +/** + * SignalCom master key packing algorithm + * + * kek stored in 3 files - kek.opq, mk.db3, masks.db3 + * kek.opq - always 36 bytes length = 32 bytes encrypted kek + 4 bytes mac of decrypted kek + * mk.db3 - 6 bytes header (1 byte magic code 0x22 + 1 byte count of masks + 4 bytes mac of + * xor summarizing masks value) + attached masks + * masks.db3 - detached masks. + * Total length of attached + detached masks = 32 bits * count of masks + * Default value of count 8 = (7 attached + 1 detached). But really no reason for such + * separation - all masks xor summarizing - order is not matter. + * Content of file rand.opq can used as ukm. Don't forget change file content after using. + * + * For usb-token files has names: + * a001 - mk.db3, b001 - masks.db3, c001 - kek.opq, d001 - rand.opq + * For windows registry + * 00000001 - mk.db3, 00000002 - masks.db3, 00000003 - key.opq, 00000004 - rand.opq, + * 00000006 - keys\00000001.key, 0000000A - certificate + * + * @memberOf GostCipher + * @method packKey + * @instance + * @private + * @param {CryptoOperationData} unpacked - clear main key 32 bytes + * @param {CryptoOperationData} ukm - random vector for packing - 32 bytes * (count of masks - 1) + * @returns {CryptoOperationData} packed master key - concatination of mk.db3 + masks.db3 + */ +function packKeySC(unpacked, ukm) // +{ + var m = this.blockSize >> 1, k = this.keySize; + var mcount = 8; + var key = new Uint8Array(buffer(unpacked)); + if (key.byteLength !== k) + throw new DataError('Wrong cleartext size ' + key.byteLength + ' bytes'); + // Check or generate UKM + ukm = ukm || this.ukm; + if (ukm) { + ukm = new Uint8Array(buffer(ukm)); + if (ukm.byteLength > 0 && ukm.byteLength % k === 0) + mcount = ukm.byteLength / k + 1; + else + throw new DataError('Wrong rand size ' + ukm.byteLength + ' bytes'); + } else + randomSeed(ukm = new Uint8Array((mcount - 1) * k)); + // Output array + var d = new Uint8Array(mcount * k + m + 2), b = d.buffer; + // Calculate MAC + var zero32 = new Uint8Array(k); + var mac = signMAC.call(this, key, zero32); + d[0] = 0x22; // Magic code + d[1] = mcount; // Count of masks + d.set(new Uint8Array(mac), 2); + d.set(ukm, k + m + 2); + for (var i = 1; i < mcount; i++) { + var mask = new Uint8Array(b, 2 + m + k * i); + for (var j = 0; j < k; j++) + key[j] ^= mask[j]; + } + d.set(key, m + 2); + return d.buffer; +} // + +/** + * Algorithm name GOST 28147-SCKW

+ * + * SignalCom master key unpacking algorithm + * + * @memberOf GostCipher + * @method unpackKey + * @instance + * @private + * @param {CryptoOperationData} packed - concatination of mk.db3 + masks.db3 + * @returns {CryptoOperationData} unpacked master key + */ +function unpackKeySC(packed) // +{ + var m = this.blockSize >> 1, k = this.keySize; + var b = buffer(packed); + // Unpack master key + var magic = new Uint8Array(b, 0, 1)[0]; + if (magic !== 0x22) + throw new DataError('Invalid magic number'); + var mcount = new Uint8Array(b, 1, 1)[0]; + var mac = new Uint8Array(b, 2, m); // MAC for summarized mask + // Compute packKey xor summing for all masks + var key = new Uint8Array(k); + for (var i = 0; i < mcount; i++) { + var mask = new Uint8Array(b, 2 + m + k * i, k); + for (var j = 0; j < k; j++) + key[j] ^= mask[j]; + } + // Test MAC for packKey with default sBox on zero 32 bytes array + var zero32 = new Uint8Array(k); + var test = verifyMAC.call(this, key, mac, zero32); + if (!test) { + // Try to use different sBoxes + var names = ['E-A', 'E-B', 'E-C', 'E-D', 'E-SC']; + for (var i = 0, n = names.length; i < n; i++) { + this.sBox = sBoxes[names[i]]; + test = verifyMAC.call(this, key, mac, zero32); + if (test) + break; + } + } + if (!test) + throw new DataError('Invalid main key MAC'); + return key.buffer; +} // + +/** + * Algorithm name GOST 28147-SCKW

+ * + * SignalCom Key Wrapping algorithm + * + * @memberOf GostCipher + * @method wrapKey + * @instance + * @param {CryptoOperationData} kek - clear kek or concatination of mk.db3 + masks.db3 + * @param {CryptoOperationData} cek - key for wrapping + * @returns {CryptoOperationData} wrapped key - file kek.opq + */ +function wrapKeySC(kek, cek) // +{ + var m = this.blockSize >> 1, n = this.keySize; + var k = buffer(kek); + var c = buffer(cek); + if (k.byteLength !== n) + k = unpackKeySC.call(this, k); + var enc = encryptECB.call(this, k, c); + var mac = signMAC.call(this, k, c); + var d = new Uint8Array(m + n); + d.set(new Uint8Array(enc), 0); + d.set(new Uint8Array(mac), n); + return d.buffer; +} // + +/** + * Algorithm name GOST 28147-SCKW

+ * + * SignalCom Key UnWrapping algorithm + * + * @memberOf GostCipher + * @method unwrapKey + * @instance + * @param {CryptoOperationData} kek - concatination of files mk.db3 + masks.db3 or clear kek + * @param {CryptoOperationData} cek - wrapping key - file kek.opq + * @return {CryptoOperationData} result + */ +function unwrapKeySC(kek, cek) // +{ + var m = this.blockSize >> 1, n = this.keySize; + var k = buffer(kek); + var c = buffer(cek); + if (k.byteLength !== n) + k = unpackKeySC.call(this, k); + var enc = new Uint8Array(c, 0, n); // Encrypted kek + var mac = new Uint8Array(c, n, m); // MAC for clear kek + var d = decryptECB.call(this, k, enc); + if (!verifyMAC.call(this, k, mac, d)) + throw new DataError('Invalid key MAC'); + return d; +} // + +/** + * Algorithm name GOST 28147-SCKW

+ * + * SignalCom master key generation for wrapping + * + * @memberOf GostCipher + * @method generateKey + * @instance + * @return {CryptoOperationData} result + */ +function generateWrappingKeySC() // +{ + return packKeySC.call(this, generateKey.call(this)); +} // + +function maskKey(mask, key, inverse, keySize) // +{ + var k = keySize / 4, + m32 = new Int32Array(buffer(mask)), + k32 = new Int32Array(buffer(key)), + r32 = new Int32Array(k); + if (inverse) + for (var i = 0; i < k; i++) + r32[i] = (k32[i] + m32[i]) & 0xffffffff; + else + for (var i = 0; i < k; i++) + r32[i] = (k32[i] - m32[i]) & 0xffffffff; + return r32.buffer; +} // + +/** + * Algorithm name GOST 28147-MASK

+ * + * This algorithm wrap key mask + * + * @memberOf GostCipher + * @method wrapKey + * @instance + * @param {CryptoOperationData} mask The mask + * @param {CryptoOperationData} key The key + * @returns {CryptoOperationData} The masked key + */ +function wrapKeyMask(mask, key) // +{ + return maskKey(mask, key, this.procreator === 'VN', this.keySize); +} // + +/** + * Algorithm name GOST 28147-CPKW

+ * + * This algorithm unwrap key mask + * + * @memberOf GostCipher + * @method unwrapKey + * @instance + * @param {CryptoOperationData} mask The mask + * @param {CryptoOperationData} key The masked key + * @return {CryptoOperationData} result The key + */ +function unwrapKeyMask(mask, key) // +{ + return maskKey(mask, key, this.procreator !== 'VN', this.keySize); +} // + +/** + * Algorithm name GOST 28147-CPKM

+ * + * Key meshing in according to rfc4357 2.3.2. CryptoPro Key Meshing + * + * @memberOf GostCipher + * @method keyMeshing + * @instance + * @private + * @param {(Uint8Array|CryptoOperationData)} k 8x8 bit key + * @param {Uint8Array} s 8x8 bit sync (iv) + * @param {Integer} i block index + * @param {Int32Array} key 8x32 bit key schedule + * @param {boolean} e true - decrypt + * @returns CryptoOperationData next 8x8 bit key + */ +function keyMeshingCP(k, s, i, key, e) // +{ + if ((i + 1) * this.blockSize % 1024 === 0) { // every 1024 octets + // K[i+1] = decryptECB (K[i], C); + k = decryptECB.call(this, k, C); + // IV0[i+1] = encryptECB (K[i+1],IVn[i]) + s.set(new Uint8Array(encryptECB.call(this, k, s))); + // restore key schedule + key.set(this.keySchedule(k, e)); + } + return k; +} // + +/** + * Null Key Meshing in according to rfc4357 2.3.1 + * + * @memberOf GostCipher + * @method keyMeshing + * @instance + * @private + * @param {(Uint8Array|CryptoOperationData)} k 8x8 bit key + */ +function noKeyMeshing(k) // +{ + return k; +} // + +/** + * Algorithm name GOST 28147-NoPadding

+ * + * No padding. + * + * @memberOf GostCipher + * @method padding + * @instance + * @private + * @param {Uint8Array} d array with source data + * @returns {Uint8Array} result + */ +function noPad(d) // +{ + return new Uint8Array(d); +} // + +/** + * Algorithm name GOST 28147-PKCS5Padding

+ * + * PKCS#5 padding: 8-x remaining bytes are filled with the value of + * 8-x. If there’s no incomplete block, one extra block filled with + * value 8 is added + * + * @memberOf GostCipher + * @method padding + * @instance + * @private + * @param {Uint8Array} d array with source data + * @returns {Uint8Array} result + */ +function pkcs5Pad(d) // +{ + var n = d.byteLength, + nb = this.blockSize, + q = nb - n % nb, + m = Math.ceil((n + 1) / nb) * nb, + r = new Uint8Array(m); + r.set(d); + for (var i = n; i < m; i++) + r[i] = q; + return r; +} // + +function pkcs5Unpad(d) // +{ + var m = d.byteLength, + nb = this.blockSize, + q = d[m - 1], + n = m - q; + if (q > nb) + throw DataError('Invalid padding'); + var r = new Uint8Array(n); + if (n > 0) + r.set(new Uint8Array(d.buffer, 0, n)); + return r; +} // + + +/** + * Algorithm name GOST 28147-ZeroPadding

+ * + * Zero padding: 8-x remaining bytes are filled with zero + * + * @memberOf GostCipher + * @method padding + * @instance + * @private + * @param {Uint8Array} d array with source data + * @returns {Uint8Array} result + */ +function zeroPad(d) // +{ + var n = d.byteLength, + nb = this.blockSize, + m = Math.ceil(n / nb) * nb, + r = new Uint8Array(m); + r.set(d); + for (var i = n; i < m; i++) + r[i] = 0; + return r; +} // + + +/** + * Algorithm name GOST 28147-BitPadding

+ * + * Bit padding: P* = P || 1 || 000...0 If there’s no incomplete block, + * one extra block filled with 1 || 000...0 + * + * @memberOf GostCipher + * @method padding + * @instance + * @private + * @param {Uint8Array} d array with source data + * @returns {Uint8Array} result + */ +function bitPad(d) // +{ + var n = d.byteLength, + nb = this.blockSize, + m = Math.ceil((n + 1) / nb) * nb, + r = new Uint8Array(m); + r.set(d); + r[n] = 1; + for (var i = n + 1; i < m; i++) + r[i] = 0; + return r; +} // + +function bitUnpad(d) // +{ + var m = d.byteLength, + n = m; + while (n > 1 && d[n - 1] === 0) + n--; + if (d[n - 1] !== 1) + throw DataError('Invalid padding'); + n--; + var r = new Uint8Array(n); + if (n > 0) + r.set(new Uint8Array(d.buffer, 0, n)); + return r; +} // + +/** + * Algorithm name GOST 28147-RandomPadding

+ * + * Random padding: 8-x remaining bytes of the last block are set to + * random. + * + * @memberOf GostCipher + * @method padding + * @instance + * @private + * @param {Uint8Array} d array with source data + * @returns {Uint8Array} result + */ +function randomPad(d) // +{ + var n = d.byteLength, + nb = this.blockSize, + q = nb - n % nb, + m = Math.ceil(n / nb) * nb, + r = new Uint8Array(m), e = new Uint8Array(r.buffer, n, q); + r.set(d); + randomSeed(e); + return r; +} // + +/** + * GOST 28147-89 Encryption Algorithm

+ * + * References {@link http://tools.ietf.org/html/rfc5830}

+ * + * When keys and initialization vectors are converted to/from byte arrays, + * little-endian byte order is assumed.

+ * + * Normalized algorithm identifier common parameters: + * + *
    + *
  • name Algorithm name 'GOST 28147' or 'GOST R 34.12'
  • + *
  • version Algorithm version, number + *
      + *
    • 1989 Current version of standard
    • + *
    • 2015 New draft version of standard
    • + *
    + *
  • + *
  • length Block length + *
      + *
    • 64 64 bits length (default)
    • + *
    • 128 128 bits length (only for version 2015)
    • + *
    + *
  • + *
  • mode Algorithm mode, string + *
      + *
    • ES Encryption mode (default)
    • + *
    • MAC "imitovstavka" (MAC) mode
    • + *
    • KW Key wrapping mode
    • + *
    + *
  • + *
  • sBox Paramset sBox for GOST 28147-89, string. Used only if version = 1989
  • + *
+ * + * Supported algorithms, modes and parameters: + * + *
    + *
  • Encript/Decrypt mode (ES) + *
      + *
    • block Block mode, string. Default ECB
    • + *
    • keyMeshing Key meshing mode, string. Default NO
    • + *
    • padding Padding mode, string. Default NO for CFB and CTR modes, or ZERO for others
    • + *
    • iv {@link CryptoOperationData} Initial vector with length of block. Default - zero block
    • + *
    + *
  • + *
  • Sign/Verify mode (MAC) + *
      + *
    • macLength Length of mac in bits (default - 32 bits)
    • + *
    • iv {@link CryptoOperationData} Initial vector with length of block. Default - zero block
    • + *
    + *
  • + *
  • Wrap/Unwrap key mode (KW) + *
      + *
    • keyWrapping Mode of keywrapping, string. Default NO - standard GOST key wrapping
    • + *
    • ukm {@link CryptoOperationData} User key material. Default - random generated value
    • + *
    + *
  • + *
+ * + * Supported paramters values: + * + *
    + *
  • Block modes (parameter 'block') + *
      + *
    • ECB "prostaya zamena" (ECB) mode (default)
    • + *
    • CFB "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode
    • + *
    • OFB "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode
    • + *
    • CTR "gammirovanie" (counter) mode
    • + *
    • CBC Cipher-Block-Chaining (CBC) mode
    • + *
    + *
  • + *
  • Key meshing modes (parameter 'keyMeshing') + *
      + *
    • NO No key wrapping (default)
    • + *
    • CP CryptoPor Key key meshing
    • + *
    + *
  • + *
  • Padding modes (parameter 'padding') + *
      + *
    • NO No padding only for CFB, OFB and CTR modes
    • + *
    • PKCS5 PKCS#5 padding mode
    • + *
    • ZERO Zero bits padding mode
    • + *
    • RANDOM Random bits padding mode
    • + *
    • BIT One bit padding mode
    • + *
    + *
  • + *
  • Wrapping key modes (parameter 'keyWrapping') + *
      + *
    • NO Ref. rfc4357 6.1 GOST 28147-89 Key wrapping
    • + *
    • CP CryptoPro Key wrapping mode
    • + *
    • SC SignalCom Key wrapping mode
    • + *
    + *
  • + *
+ * + * @class GostCipher + * @param {AlgorithmIndentifier} algorithm WebCryptoAPI algorithm identifier + */ +function GostCipher(algorithm) // +{ + // Check little endian support + if (!littleEndian) + throw new NotSupportedError('Big endian platform not supported'); + algorithm = algorithm || {}; + this.keySize = 32; + this.blockLength = algorithm.length || 64; + this.blockSize = this.blockLength >> 3; + + this.name = (algorithm.name || (algorithm.version === 1 ? 'RC2' : + algorithm.version === 1989 ? 'GOST 28147' : 'GOST R 34.12')) + + (algorithm.version > 4 ? '-' + ((algorithm.version || 1989) % 100) : '') + '-' + + (this.blockLength === 64 ? '' : this.blockLength + '-') + + ((algorithm.mode === 'MAC') ? 'MAC-' + (algorithm.macLength || this.blockLength >> 1) : + (algorithm.mode === 'KW' || algorithm.keyWrapping) ? + ((algorithm.keyWrapping || 'NO') !== 'NO' ? algorithm.keyWrapping : '') + 'KW' : + (algorithm.block || 'ECB') + ((algorithm.block === 'CFB' || algorithm.block === 'OFB' || + (algorithm.block === 'CTR' && algorithm.version === 2015)) && + algorithm.shiftBits && algorithm.shiftBits !== this.blockLength ? '-' + algorithm.shiftBits : '') + + (algorithm.padding ? '-' + (algorithm.padding || (algorithm.block === 'CTR' || + algorithm.block === 'CFB' || algorithm.block === 'OFB' ? 'NO' : 'ZERO')) + 'PADDING' : '') + + ((algorithm.keyMeshing || 'NO') !== 'NO' ? '-CPKEYMESHING' : '')) + + (algorithm.procreator ? '/' + algorithm.procreator : '') + + (typeof algorithm.sBox === 'string' ? '/' + algorithm.sBox : ''); + + // Algorithm procreator + this.procreator = algorithm.procreator; + + switch (algorithm.version || 1989) { + case 1: + this.process = processRC2; + this.keySchedule = keyScheduleRC2; + this.blockLength = 64; + this.effectiveLength = algorithm.length || 32; + this.keySize = 8 * Math.ceil(this.effectiveLength / 8); // Max 128 + this.blockSize = this.blockLength >> 3; + break; + case 2015: + this.version = 2015; + if (this.blockLength === 64) { + this.process = process15; + this.keySchedule = keySchedule15; + } else if (this.blockLength === 128) { + this.process = process128; + this.keySchedule = keySchedule128; + } else + throw new DataError('Invalid block length'); + this.processMAC = processMAC15; + break; + case 1989: + this.version = 1989; + this.process = process89; + this.processMAC = processMAC89; + this.keySchedule = keySchedule89; + if (this.blockLength !== 64) + throw new DataError('Invalid block length'); + break; + default: + throw new NotSupportedError('Algorithm version ' + algorithm.version + ' not supported'); + } + + switch (algorithm.mode || (algorithm.keyWrapping && 'KW') || 'ES') { + + case 'ES': + switch (algorithm.block || 'ECB') { + case 'ECB': + this.encrypt = encryptECB; + this.decrypt = decryptECB; + break; + case 'CTR': + if (this.version === 1989) { + this.encrypt = processCTR89; + this.decrypt = processCTR89; + } else { + this.encrypt = processCTR15; + this.decrypt = processCTR15; + this.shiftBits = algorithm.shiftBits || this.blockLength; + } + break + case 'CBC': + this.encrypt = encryptCBC; + this.decrypt = decryptCBC; + break + case 'CFB': + this.encrypt = encryptCFB; + this.decrypt = decryptCFB; + this.shiftBits = algorithm.shiftBits || this.blockLength; + break; + case 'OFB': + this.encrypt = processOFB; + this.decrypt = processOFB; + this.shiftBits = algorithm.shiftBits || this.blockLength; + break; + default: + throw new NotSupportedError('Block mode ' + algorithm.block + ' not supported'); + } + switch (algorithm.keyMeshing) { + case 'CP': + this.keyMeshing = keyMeshingCP; + break; + default: + this.keyMeshing = noKeyMeshing; + } + if (this.encrypt === encryptECB || this.encrypt === encryptCBC) { + switch (algorithm.padding) { + case 'PKCS5P': + this.pad = pkcs5Pad; + this.unpad = pkcs5Unpad; + break; + case 'RANDOM': + this.pad = randomPad; + this.unpad = noPad; + break; + case 'BIT': + this.pad = bitPad; + this.unpad = bitUnpad; + break; + default: + this.pad = zeroPad; + this.unpad = noPad; + } + } else { + this.pad = noPad; + this.unpad = noPad; + } + this.generateKey = generateKey; + break; + case 'MAC': + this.sign = signMAC; + this.verify = verifyMAC; + this.generateKey = generateKey; + this.macLength = algorithm.macLength || (this.blockLength >> 1); + this.pad = noPad; + this.unpad = noPad; + this.keyMeshing = noKeyMeshing; + break; + case 'KW': + this.pad = noPad; + this.unpad = noPad; + this.keyMeshing = noKeyMeshing; + switch (algorithm.keyWrapping) { + case 'CP': + this.wrapKey = wrapKeyCP; + this.unwrapKey = unwrapKeyCP; + this.generateKey = generateKey; + this.shiftBits = algorithm.shiftBits || this.blockLength; + break; + case 'SC': + this.wrapKey = wrapKeySC; + this.unwrapKey = unwrapKeySC; + this.generateKey = generateWrappingKeySC; + break; + default: + this.wrapKey = wrapKeyGOST; + this.unwrapKey = unwrapKeyGOST; + this.generateKey = generateKey; + } + break; + case 'MASK': + this.wrapKey = wrapKeyMask; + this.unwrapKey = unwrapKeyMask; + this.generateKey = generateKey; + break; + default: + throw new NotSupportedError('Mode ' + algorithm.mode + ' not supported'); + } + + // Define sBox parameter + var sBox = algorithm.sBox, sBoxName; + if (!sBox) + sBox = this.version === 2015 ? sBoxes['E-Z'] : this.procreator === 'SC' ? sBoxes['E-SC'] : sBoxes['E-A']; + else if (typeof sBox === 'string') { + sBoxName = sBox.toUpperCase(); + sBox = sBoxes[sBoxName]; + if (!sBox) + throw new SyntaxError('Unknown sBox name: ' + algorithm.sBox); + } else if (!sBox.length || sBox.length !== sBoxes['E-Z'].length) + throw new SyntaxError('Length of sBox must be ' + sBoxes['E-Z'].length); + this.sBox = sBox; + // Initial vector + if (algorithm.iv) { + this.iv = new Uint8Array(algorithm.iv); + if (this.iv.byteLength !== this.blockSize && this.version === 1989) + throw new SyntaxError('Length of iv must be ' + this.blockLength + ' bits'); + else if (this.iv.byteLength !== this.blockSize >> 1 && this.encrypt === processCTR15) + throw new SyntaxError('Length of iv must be ' + this.blockLength >> 1 + ' bits'); + else if (this.iv.byteLength % this.blockSize !== 0 && this.encrypt !== processCTR15) + throw new SyntaxError('Length of iv must be a multiple of ' + this.blockLength + ' bits'); + } else + this.iv = this.blockLength === 128 ? defaultIV128 : defaultIV; + // User key material + if (algorithm.ukm) { + this.ukm = new Uint8Array(algorithm.ukm); + if (this.ukm.byteLength * 8 !== this.blockLength) + throw new SyntaxError('Length of ukm must be ' + this.blockLength + ' bits'); + } +} // + +export default GostCipher; diff --git a/src/core/vendor/gost/gostCoding.mjs b/src/core/vendor/gost/gostCoding.mjs new file mode 100644 index 00000000..b3f2d72a --- /dev/null +++ b/src/core/vendor/gost/gostCoding.mjs @@ -0,0 +1,1160 @@ +/** + * Coding algorithms: Base64, Hex, Int16, Chars, BER and PEM + * version 1.76 + * 2014-2016, Rudolf Nickolaev. All rights reserved. + * + * Exported for CyberChef by mshwed [m@ttshwed.com] + */ + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * THIS SOfTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES Of MERCHANTABILITY AND fITNESS fOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * fOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT Of SUBSTITUTE GOODS OR + * SERVICES; LOSS Of USE, DATA, OR PROfITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY Of LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT Of THE USE + * Of THIS SOfTWARE, EVEN If ADVISED Of THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +import gostCrypto from './gostCrypto.mjs'; + +/** + * The Coding interface provides string converting methods: Base64, Hex, + * Int16, Chars, BER and PEM + * @class GostCoding + * + */ // +var root = {}; +var DataError = Error; +var CryptoOperationData = ArrayBuffer; +var Date = Date; + +function buffer(d) { + if (d instanceof CryptoOperationData) + return d; + else if (d && d.buffer && d.buffer instanceof CryptoOperationData) + return d.byteOffset === 0 && d.byteLength === d.buffer.byteLength ? + d.buffer : new Uint8Array(new Uint8Array(d, d.byteOffset, d.byteLength)).buffer; + else + throw new DataError('CryptoOperationData required'); +} // + +function GostCoding() { +} + +/** + * BASE64 conversion + * + * @class GostCoding.Base64 + */ +var Base64 = {// + /** + * Base64.decode convert BASE64 string s to CryptoOperationData + * + * @memberOf GostCoding.Base64 + * @param {String} s BASE64 encoded string value + * @returns {CryptoOperationData} Binary decoded data + */ + decode: function (s) { + s = s.replace(/[^A-Za-z0-9\+\/]/g, ''); + var n = s.length, + k = n * 3 + 1 >> 2, r = new Uint8Array(k); + + for (var m3, m4, u24 = 0, j = 0, i = 0; i < n; i++) { + m4 = i & 3; + var c = s.charCodeAt(i); + + c = c > 64 && c < 91 ? + c - 65 : c > 96 && c < 123 ? + c - 71 : c > 47 && c < 58 ? + c + 4 : c === 43 ? + 62 : c === 47 ? + 63 : 0; + + u24 |= c << 18 - 6 * m4; + if (m4 === 3 || n - i === 1) { + for (m3 = 0; m3 < 3 && j < k; m3++, j++) { + r[j] = u24 >>> (16 >>> m3 & 24) & 255; + } + u24 = 0; + + } + } + return r.buffer; + }, + /** + * Base64.encode(data) convert CryptoOperationData data to BASE64 string + * + * @memberOf GostCoding.Base64 + * @param {CryptoOperationData} data Bynary data for encoding + * @returns {String} BASE64 encoded data + */ + encode: function (data) { + var slen = 8, d = new Uint8Array(buffer(data)); + var m3 = 2, s = ''; + for (var n = d.length, u24 = 0, i = 0; i < n; i++) { + m3 = i % 3; + if (i > 0 && (i * 4 / 3) % (12 * slen) === 0) + s += '\r\n'; + u24 |= d[i] << (16 >>> m3 & 24); + if (m3 === 2 || n - i === 1) { + for (var j = 18; j >= 0; j -= 6) { + var c = u24 >>> j & 63; + c = c < 26 ? c + 65 : c < 52 ? c + 71 : c < 62 ? c - 4 : + c === 62 ? 43 : c === 63 ? 47 : 65; + s += String.fromCharCode(c); + } + u24 = 0; + } + } + return s.substr(0, s.length - 2 + m3) + (m3 === 2 ? '' : m3 === 1 ? '=' : '=='); + } // +}; + +/** + * BASE64 conversion + * + * @memberOf GostCoding + * @insnance + * @type GostCoding.Base64 + */ +GostCoding.prototype.Base64 = Base64; + +/** + * Text string conversion
+ * Methods support charsets: ascii, win1251, utf8, utf16 (ucs2, unicode), utf32 (ucs4) + * + * @class GostCoding.Chars + */ +var Chars = (function () { // + + var _win1251_ = { + 0x402: 0x80, 0x403: 0x81, 0x201A: 0x82, 0x453: 0x83, 0x201E: 0x84, 0x2026: 0x85, 0x2020: 0x86, 0x2021: 0x87, + 0x20AC: 0x88, 0x2030: 0x89, 0x409: 0x8A, 0x2039: 0x8B, 0x40A: 0x8C, 0x40C: 0x8D, 0x40B: 0x8E, 0x40f: 0x8f, + 0x452: 0x90, 0x2018: 0x91, 0x2019: 0x92, 0x201C: 0x93, 0x201D: 0x94, 0x2022: 0x95, 0x2013: 0x96, 0x2014: 0x97, + 0x2122: 0x99, 0x459: 0x9A, 0x203A: 0x9B, 0x45A: 0x9C, 0x45C: 0x9D, 0x45B: 0x9E, 0x45f: 0x9f, + 0xA0: 0xA0, 0x40E: 0xA1, 0x45E: 0xA2, 0x408: 0xA3, 0xA4: 0xA4, 0x490: 0xA5, 0xA6: 0xA6, 0xA7: 0xA7, + 0x401: 0xA8, 0xA9: 0xA9, 0x404: 0xAA, 0xAB: 0xAB, 0xAC: 0xAC, 0xAD: 0xAD, 0xAE: 0xAE, 0x407: 0xAf, + 0xB0: 0xB0, 0xB1: 0xB1, 0x406: 0xB2, 0x456: 0xB3, 0x491: 0xB4, 0xB5: 0xB5, 0xB6: 0xB6, 0xB7: 0xB7, + 0x451: 0xB8, 0x2116: 0xB9, 0x454: 0xBA, 0xBB: 0xBB, 0x458: 0xBC, 0x405: 0xBD, 0x455: 0xBE, 0x457: 0xBf + }; + var _win1251back_ = {}; + for (var from in _win1251_) { + var to = _win1251_[from]; + _win1251back_[to] = from; + } + + return { + /** + * Chars.decode(s, charset) convert string s with defined charset to CryptoOperationData + * + * @memberOf GostCoding.Chars + * @param {string} s Javascript string + * @param {string} charset Charset, default 'win1251' + * @returns {CryptoOperationData} Decoded binary data + */ + decode: function (s, charset) { + charset = (charset || 'win1251').toLowerCase().replace('-', ''); + var r = []; + for (var i = 0, j = s.length; i < j; i++) { + var c = s.charCodeAt(i); + if (charset === 'utf8') { + if (c < 0x80) { + r.push(c); + } else if (c < 0x800) { + r.push(0xc0 + (c >>> 6)); + r.push(0x80 + (c & 63)); + } else if (c < 0x10000) { + r.push(0xe0 + (c >>> 12)); + r.push(0x80 + (c >>> 6 & 63)); + r.push(0x80 + (c & 63)); + } else if (c < 0x200000) { + r.push(0xf0 + (c >>> 18)); + r.push(0x80 + (c >>> 12 & 63)); + r.push(0x80 + (c >>> 6 & 63)); + r.push(0x80 + (c & 63)); + } else if (c < 0x4000000) { + r.push(0xf8 + (c >>> 24)); + r.push(0x80 + (c >>> 18 & 63)); + r.push(0x80 + (c >>> 12 & 63)); + r.push(0x80 + (c >>> 6 & 63)); + r.push(0x80 + (c & 63)); + } else { + r.push(0xfc + (c >>> 30)); + r.push(0x80 + (c >>> 24 & 63)); + r.push(0x80 + (c >>> 18 & 63)); + r.push(0x80 + (c >>> 12 & 63)); + r.push(0x80 + (c >>> 6 & 63)); + r.push(0x80 + (c & 63)); + } + } else if (charset === 'unicode' || charset === 'ucs2' || charset === 'utf16') { + if (c < 0xD800 || (c >= 0xE000 && c <= 0x10000)) { + r.push(c >>> 8); + r.push(c & 0xff); + } else if (c >= 0x10000 && c < 0x110000) { + c -= 0x10000; + var first = ((0xffc00 & c) >> 10) + 0xD800; + var second = (0x3ff & c) + 0xDC00; + r.push(first >>> 8); + r.push(first & 0xff); + r.push(second >>> 8); + r.push(second & 0xff); + } + } else if (charset === 'utf32' || charset === 'ucs4') { + r.push(c >>> 24 & 0xff); + r.push(c >>> 16 & 0xff); + r.push(c >>> 8 & 0xff); + r.push(c & 0xff); + } else if (charset === 'win1251') { + if (c >= 0x80) { + if (c >= 0x410 && c < 0x450) // А..Яа..я + c -= 0x350; + else + c = _win1251_[c] || 0; + } + r.push(c); + } else + r.push(c & 0xff); + } + return new Uint8Array(r).buffer; + }, + /** + * Chars.encode(data, charset) convert CryptoOperationData data to string with defined charset + * + * @memberOf GostCoding.Chars + * @param {CryptoOperationData} data Binary data + * @param {string} charset Charset, default win1251 + * @returns {string} Encoded javascript string + */ + encode: function (data, charset) { + charset = (charset || 'win1251').toLowerCase().replace('-', ''); + var r = [], d = new Uint8Array(buffer(data)); + for (var i = 0, n = d.length; i < n; i++) { + var c = d[i]; + if (charset === 'utf8') { + c = c >= 0xfc && c < 0xfe && i + 5 < n ? // six bytes + (c - 0xfc) * 1073741824 + (d[++i] - 0x80 << 24) + (d[++i] - 0x80 << 18) + (d[++i] - 0x80 << 12) + (d[++i] - 0x80 << 6) + d[++i] - 0x80 + : c >> 0xf8 && c < 0xfc && i + 4 < n ? // five bytes + (c - 0xf8 << 24) + (d[++i] - 0x80 << 18) + (d[++i] - 0x80 << 12) + (d[++i] - 0x80 << 6) + d[++i] - 0x80 + : c >> 0xf0 && c < 0xf8 && i + 3 < n ? // four bytes + (c - 0xf0 << 18) + (d[++i] - 0x80 << 12) + (d[++i] - 0x80 << 6) + d[++i] - 0x80 + : c >= 0xe0 && c < 0xf0 && i + 2 < n ? // three bytes + (c - 0xe0 << 12) + (d[++i] - 0x80 << 6) + d[++i] - 0x80 + : c >= 0xc0 && c < 0xe0 && i + 1 < n ? // two bytes + (c - 0xc0 << 6) + d[++i] - 0x80 + : c; // one byte + } else if (charset === 'unicode' || charset === 'ucs2' || charset === 'utf16') { + c = (c << 8) + d[++i]; + if (c >= 0xD800 && c < 0xE000) { + var first = (c - 0xD800) << 10; + c = d[++i]; + c = (c << 8) + d[++i]; + var second = c - 0xDC00; + c = first + second + 0x10000; + } + } else if (charset === 'utf32' || charset === 'ucs4') { + c = (c << 8) + d[++i]; + c = (c << 8) + d[++i]; + c = (c << 8) + d[++i]; + } else if (charset === 'win1251') { + if (c >= 0x80) { + if (c >= 0xC0 && c < 0x100) + c += 0x350; // А..Яа..я + else + c = _win1251back_[c] || 0; + } + } + r.push(String.fromCharCode(c)); + } + return r.join(''); + } + }; // +})(); + +/** + * Text string conversion + * + * @memberOf GostCoding + * @insnance + * @type GostCoding.Chars + */ +GostCoding.prototype.Chars = Chars; + +/** + * HEX conversion + * + * @class GostCoding.Hex + */ +var Hex = {// + /** + * Hex.decode(s, endean) convert HEX string s to CryptoOperationData in endean mode + * + * @memberOf GostCoding.Hex + * @param {string} s Hex encoded string + * @param {boolean} endean Little or Big Endean, default Little + * @returns {CryptoOperationData} Decoded binary data + */ + decode: function (s, endean) { + s = s.replace(/[^A-fa-f0-9]/g, ''); + var n = Math.ceil(s.length / 2), r = new Uint8Array(n); + s = (s.length % 2 > 0 ? '0' : '') + s; + if (endean && ((typeof endean !== 'string') || + (endean.toLowerCase().indexOf('little') < 0))) + for (var i = 0; i < n; i++) + r[i] = parseInt(s.substr((n - i - 1) * 2, 2), 16); + else + for (var i = 0; i < n; i++) + r[i] = parseInt(s.substr(i * 2, 2), 16); + return r.buffer; + }, + /** + * Hex.encode(data, endean) convert CryptoOperationData data to HEX string in endean mode + * + * @memberOf GostCoding.Hex + * @param {CryptoOperationData} data Binary data + * @param {boolean} endean Little/Big Endean, default Little + * @returns {string} Hex decoded string + */ + encode: function (data, endean) { + var s = [], d = new Uint8Array(buffer(data)), n = d.length; + if (endean && ((typeof endean !== 'string') || + (endean.toLowerCase().indexOf('little') < 0))) + for (var i = 0; i < n; i++) { + var j = n - i - 1; + s[j] = (j > 0 && j % 32 === 0 ? '\r\n' : '') + + ('00' + d[i].toString(16)).slice(-2); + } + else + for (var i = 0; i < n; i++) + s[i] = (i > 0 && i % 32 === 0 ? '\r\n' : '') + + ('00' + d[i].toString(16)).slice(-2); + return s.join(''); + } // +}; + +/** + * HEX conversion + * @memberOf GostCoding + * @insnance + * @type GostCoding.Hex + */ +GostCoding.prototype.Hex = Hex; + +/** + * String hex-encoded integer conversion + * + * @class GostCoding.Int16 + */ +var Int16 = {// + /** + * Int16.decode(s) convert hex big insteger s to CryptoOperationData + * + * @memberOf GostCoding.Int16 + * @param {string} s Int16 string + * @returns {CryptoOperationData} Decoded binary data + */ + decode: function (s) { + s = (s || '').replace(/[^\-A-fa-f0-9]/g, ''); + if (s.length === 0) + s = '0'; + // Signature + var neg = false; + if (s.charAt(0) === '-') { + neg = true; + s = s.substring(1); + } + // Align 2 chars + while (s.charAt(0) === '0' && s.length > 1) + s = s.substring(1); + s = (s.length % 2 > 0 ? '0' : '') + s; + // Padding for singanuture + // '800000' - 'ffffff' - for positive + // '800001' - 'ffffff' - for negative + if ((!neg && !/^[0-7]/.test(s)) || + (neg && !/^[0-7]|8[0]+$/.test(s))) + s = '00' + s; + // Convert hex + var n = s.length / 2, r = new Uint8Array(n), t = 0; + for (var i = n - 1; i >= 0; --i) { + var c = parseInt(s.substr(i * 2, 2), 16); + if (neg && (c + t > 0)) { + c = 256 - c - t; + t = 1; + } + r[i] = c; + } + return r.buffer; + }, + /** + * Int16.encode(data) convert CryptoOperationData data to big integer hex string + * + * @memberOf GostCoding.Int16 + * @param {CryptoOperationData} data Binary data + * @returns {string} Int16 encoded string + */ + encode: function (data) { + var d = new Uint8Array(buffer(data)), n = d.length; + if (d.length === 0) + return '0x00'; + var s = [], neg = d[0] > 0x7f, t = 0; + for (var i = n - 1; i >= 0; --i) { + var v = d[i]; + if (neg && (v + t > 0)) { + v = 256 - v - t; + t = 1; + } + s[i] = ('00' + v.toString(16)).slice(-2); + } + s = s.join(''); + while (s.charAt(0) === '0') + s = s.substring(1); + return (neg ? '-' : '') + '0x' + s; + } // +}; + +/** + * String hex-encoded integer conversion + * @memberOf GostCoding + * @insnance + * @type GostCoding.Int16 + */ +GostCoding.prototype.Int16 = Int16; + +/** + * BER, DER, CER conversion + * + * @class GostCoding.BER + */ +var BER = (function () { // + + // Predefenition block + function encodeBER(source, format, onlyContent) { + // Correct primitive type + var object = source.object; + if (object === undefined) + object = source; + + // Determinate tagClass + var tagClass = source.tagClass = source.tagClass || 0; // Universial default + + // Determinate tagNumber. Use only for Universal class + if (tagClass === 0) { + var tagNumber = source.tagNumber; + if (typeof tagNumber === 'undefined') { + if (typeof object === 'string') { + if (object === '') // NULL + tagNumber = 0x05; + else if (/^\-?0x[0-9a-fA-F]+$/.test(object)) // INTEGER + tagNumber = 0x02; + else if (/^(\d+\.)+\d+$/.test(object)) // OID + tagNumber = 0x06; + else if (/^[01]+$/.test(object)) // BIT STRING + tagNumber = 0x03; + else if (/^(true|false)$/.test(object)) // BOOLEAN + tagNumber = 0x01; + else if (/^[0-9a-fA-F]+$/.test(object)) // OCTET STRING + tagNumber = 0x04; + else + tagNumber = 0x13; // Printable string (later can be changed to UTF8String) + } else if (typeof object === 'number') { // INTEGER + tagNumber = 0x02; + } else if (typeof object === 'boolean') { // BOOLEAN + tagNumber = 0x01; + } else if (object instanceof Array) { // SEQUENCE + tagNumber = 0x10; + } else if (object instanceof Date) { // GeneralizedTime + tagNumber = 0x18; + } else if (object instanceof CryptoOperationData || (object && object.buffer instanceof CryptoOperationData)) { + tagNumber = 0x04; + } else + throw new DataError('Unrecognized type for ' + object); + } + } + + // Determinate constructed + var tagConstructed = source.tagConstructed; + if (typeof tagConstructed === 'undefined') + tagConstructed = source.tagConstructed = object instanceof Array; + + // Create content + var content; + if (object instanceof CryptoOperationData || (object && object.buffer instanceof CryptoOperationData)) { // Direct + content = new Uint8Array(buffer(object)); + if (tagNumber === 0x03) { // BITSTRING + // Set unused bits + var a = new Uint8Array(buffer(content)); + content = new Uint8Array(a.length + 1); + content[0] = 0; // No unused bits + content.set(a, 1); + } + } else if (tagConstructed) { // Sub items coding + if (object instanceof Array) { + var bytelen = 0, ba = [], offset = 0; + for (var i = 0, n = object.length; i < n; i++) { + ba[i] = encodeBER(object[i], format); + bytelen += ba[i].length; + } + if (tagNumber === 0x11) + ba.sort(function (a, b) { // Sort order for SET components + for (var i = 0, n = Math.min(a.length, b.length); i < n; i++) { + var r = a[i] - b[i]; + if (r !== 0) + return r; + } + return a.length - b.length; + }); + if (format === 'CER') { // final for CER 00 00 + ba[n] = new Uint8Array(2); + bytelen += 2; + } + content = new Uint8Array(bytelen); + for (var i = 0, n = ba.length; i < n; i++) { + content.set(ba[i], offset); + offset = offset + ba[i].length; + } + } else + throw new DataError('Constracted block can\'t be primitive'); + } else { + switch (tagNumber) { + // 0x00: // EOC + case 0x01: // BOOLEAN + content = new Uint8Array(1); + content[0] = object ? 0xff : 0; + break; + case 0x02: // INTEGER + case 0x0a: // ENUMIRATED + content = Int16.decode( + typeof object === 'number' ? object.toString(16) : object); + break; + case 0x03: // BIT STRING + if (typeof object === 'string') { + var unusedBits = 7 - (object.length + 7) % 8; + var n = Math.ceil(object.length / 8); + content = new Uint8Array(n + 1); + content[0] = unusedBits; + for (var i = 0; i < n; i++) { + var c = 0; + for (var j = 0; j < 8; j++) { + var k = i * 8 + j; + c = (c << 1) + (k < object.length ? (object.charAt(k) === '1' ? 1 : 0) : 0); + } + content[i + 1] = c; + } + } + break; + case 0x04: + content = Hex.decode( + typeof object === 'number' ? object.toString(16) : object); + break; + // case 0x05: // NULL + case 0x06: // OBJECT IDENTIFIER + var a = object.match(/\d+/g), r = []; + for (var i = 1; i < a.length; i++) { + var n = +a[i], r1 = []; + if (i === 1) + n = n + a[0] * 40; + do { + r1.push(n & 0x7F); + n = n >>> 7; + } while (n); + // reverse order + for (j = r1.length - 1; j >= 0; --j) + r.push(r1[j] + (j === 0 ? 0x00 : 0x80)); + } + content = new Uint8Array(r); + break; + // case 0x07: // ObjectDescriptor + // case 0x08: // EXTERNAL + // case 0x09: // REAL + // case 0x0A: // ENUMERATED + // case 0x0B: // EMBEDDED PDV + case 0x0C: // UTF8String + content = Chars.decode(object, 'utf8'); + break; + // case 0x10: // SEQUENCE + // case 0x11: // SET + case 0x12: // NumericString + case 0x16: // IA5String // ASCII + case 0x13: // PrintableString // ASCII subset + case 0x14: // TeletexString // aka T61String + case 0x15: // VideotexString + case 0x19: // GraphicString + case 0x1A: // VisibleString // ASCII subset + case 0x1B: // GeneralString + // Reflect on character encoding + for (var i = 0, n = object.length; i < n; i++) + if (object.charCodeAt(i) > 255) + tagNumber = 0x0C; + if (tagNumber === 0x0C) + content = Chars.decode(object, 'utf8'); + else + content = Chars.decode(object, 'ascii'); + break; + case 0x17: // UTCTime + case 0x18: // GeneralizedTime + var result = object.original; + if (!result) { + var date = new Date(object); + date.setMinutes(date.getMinutes() + date.getTimezoneOffset()); // to UTC + var ms = tagNumber === 0x18 ? date.getMilliseconds().toString() : ''; // Milliseconds, remove trailing zeros + while (ms.length > 0 && ms.charAt(ms.length - 1) === '0') + ms = ms.substring(0, ms.length - 1); + if (ms.length > 0) + ms = '.' + ms; + result = (tagNumber === 0x17 ? date.getYear().toString().slice(-2) : date.getFullYear().toString()) + + ('00' + (date.getMonth() + 1)).slice(-2) + + ('00' + date.getDate()).slice(-2) + + ('00' + date.getHours()).slice(-2) + + ('00' + date.getMinutes()).slice(-2) + + ('00' + date.getSeconds()).slice(-2) + ms + 'Z'; + } + content = Chars.decode(result, 'ascii'); + break; + case 0x1C: // UniversalString + content = Chars.decode(object, 'utf32'); + break; + case 0x1E: // BMPString + content = Chars.decode(object, 'utf16'); + break; + } + } + + if (!content) + content = new Uint8Array(0); + if (content instanceof CryptoOperationData) + content = new Uint8Array(content); + + if (!tagConstructed && format === 'CER') { + // Encoding CER-form for string types + var k; + switch (tagNumber) { + case 0x03: // BIT_STRING + k = 1; // ingnore unused bit for bit string + case 0x04: // OCTET_STRING + case 0x0C: // UTF8String + case 0x12: // NumericString + case 0x13: // PrintableString + case 0x14: // TeletexString + case 0x15: // VideotexString + case 0x16: // IA5String + case 0x19: // GraphicString + case 0x1A: // VisibleString + case 0x1B: // GeneralString + case 0x1C: // UniversalString + case 0x1E: // BMPString + k = k || 0; + // Split content on 1000 octet len parts + var size = 1000; + var bytelen = 0, ba = [], offset = 0; + for (var i = k, n = content.length; i < n; i += size - k) { + ba[i] = encodeBER({ + object: new Unit8Array(content.buffer, i, Math.min(size - k, n - i)), + tagNumber: tagNumber, + tagClass: 0, + tagConstructed: false + }, format); + bytelen += ba[i].length; + } + ba[n] = new Uint8Array(2); // final for CER 00 00 + bytelen += 2; + content = new Uint8Array(bytelen); + for (var i = 0, n = ba.length; i < n; i++) { + content.set(ba[i], offset); + offset = offset + ba[i].length; + } + } + } + + // Restore tagNumber for all classes + if (tagClass === 0) + source.tagNumber = tagNumber; + else + source.tagNumber = tagNumber = source.tagNumber || 0; + source.content = content; + + if (onlyContent) + return content; + + // Create header + // tagNumber + var ha = [], first = tagClass === 3 ? 0xC0 : tagClass === 2 ? 0x80 : + tagClass === 1 ? 0x40 : 0x00; + if (tagConstructed) + first |= 0x20; + if (tagNumber < 0x1F) { + first |= tagNumber & 0x1F; + ha.push(first); + } else { + first |= 0x1F; + ha.push(first); + var n = tagNumber, ha1 = []; + do { + ha1.push(n & 0x7F); + n = n >>> 7; + } while (n) + // reverse order + for (var j = ha1.length - 1; j >= 0; --j) + ha.push(ha1[j] + (j === 0 ? 0x00 : 0x80)); + } + // Length + if (tagConstructed && format === 'CER') { + ha.push(0x80); + } else { + var len = content.length; + if (len > 0x7F) { + var l2 = len, ha2 = []; + do { + ha2.push(l2 & 0xff); + l2 = l2 >>> 8; + } while (l2); + ha.push(ha2.length + 0x80); // reverse order + for (var j = ha2.length - 1; j >= 0; --j) + ha.push(ha2[j]); + } else { + // simple len + ha.push(len); + } + } + var header = source.header = new Uint8Array(ha); + + // Result - complete buffer + var block = new Uint8Array(header.length + content.length); + block.set(header, 0); + block.set(content, header.length); + return block; + } + + function decodeBER(source, offset) { + + // start pos + var pos = offset || 0, start = pos; + var tagNumber, tagClass, tagConstructed, + content, header, buffer, sub, len; + + if (source.object) { + // Ready from source + tagNumber = source.tagNumber; + tagClass = source.tagClass; + tagConstructed = source.tagConstructed; + content = source.content; + header = source.header; + buffer = source.object instanceof CryptoOperationData ? + new Uint8Array(source.object) : null; + sub = source.object instanceof Array ? source.object : null; + len = buffer && buffer.length || null; + } else { + // Decode header + var d = source; + + // Read tag + var buf = d[pos++]; + tagNumber = buf & 0x1f; + tagClass = buf >> 6; + tagConstructed = (buf & 0x20) !== 0; + if (tagNumber === 0x1f) { // long tag + tagNumber = 0; + do { + if (tagNumber > 0x1fffffffffff80) + throw new DataError('Convertor not supported tag number more then (2^53 - 1) at position ' + offset); + buf = d[pos++]; + tagNumber = (tagNumber << 7) + (buf & 0x7f); + } while (buf & 0x80); + } + + // Read len + buf = d[pos++]; + len = buf & 0x7f; + if (len !== buf) { + if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways + throw new DataError('Length over 48 bits not supported at position ' + offset); + if (len === 0) + len = null; // undefined + else { + buf = 0; + for (var i = 0; i < len; ++i) + buf = (buf << 8) + d[pos++]; + len = buf; + } + } + + start = pos; + sub = null; + + if (tagConstructed) { + // must have valid content + sub = []; + if (len !== null) { + // definite length + var end = start + len; + while (pos < end) { + var s = decodeBER(d, pos); + sub.push(s); + pos += s.header.length + s.content.length; + } + if (pos !== end) + throw new DataError('Content size is not correct for container starting at offset ' + start); + } else { + // undefined length + try { + for (; ; ) { + var s = decodeBER(d, pos); + pos += s.header.length + s.content.length; + if (s.tagClass === 0x00 && s.tagNumber === 0x00) + break; + sub.push(s); + } + len = pos - start; + } catch (e) { + throw new DataError('Exception ' + e + ' while decoding undefined length content at offset ' + start); + } + } + } + + // Header and content + header = new Uint8Array(d.buffer, offset, start - offset); + content = new Uint8Array(d.buffer, start, len); + buffer = content; + } + + // Constructed types - check for string concationation + if (sub !== null && tagClass === 0) { + var k; + switch (tagNumber) { + case 0x03: // BIT_STRING + k = 1; // ingnore unused bit for bit string + case 0x04: // OCTET_STRING + case 0x0C: // UTF8String + case 0x12: // NumericString + case 0x13: // PrintableString + case 0x14: // TeletexString + case 0x15: // VideotexString + case 0x16: // IA5String + case 0x19: // GraphicString + case 0x1A: // VisibleString + case 0x1B: // GeneralString + case 0x1C: // UniversalString + case 0x1E: // BMPString + k = k || 0; + // Concatination + if (sub.length === 0) + throw new DataError('No constructed encoding content of string type at offset ' + start); + len = k; + for (var i = 0, n = sub.length; i < n; i++) { + var s = sub[i]; + if (s.tagClass !== tagClass || s.tagNumber !== tagNumber || s.tagConstructed) + throw new DataError('Invalid constructed encoding of string type at offset ' + start); + len += s.content.length - k; + } + buffer = new Uint8Array(len); + for (var i = 0, n = sub.length, j = k; i < n; i++) { + var s = sub[i]; + if (k > 0) + buffer.set(s.content.subarray(1), j); + else + buffer.set(s.content, j); + j += s.content.length - k; + } + tagConstructed = false; // follow not required + sub = null; + break; + } + } + // Primitive types + var object = ''; + if (sub === null) { + if (len === null) + throw new DataError('Invalid tag with undefined length at offset ' + start); + + if (tagClass === 0) { + switch (tagNumber) { + case 0x01: // BOOLEAN + object = buffer[0] !== 0; + break; + case 0x02: // INTEGER + case 0x0a: // ENUMIRATED + if (len > 6) { + object = Int16.encode(buffer); + } else { + var v = buffer[0]; + if (buffer[0] > 0x7f) + v = v - 256; + for (var i = 1; i < len; i++) + v = v * 256 + buffer[i]; + object = v; + } + break; + case 0x03: // BIT_STRING + if (len > 5) { // Content buffer + object = new Uint8Array(buffer.subarray(1)).buffer; + } else { // Max bit mask only for 32 bit + var unusedBit = buffer[0], + skip = unusedBit, s = []; + for (var i = len - 1; i >= 1; --i) { + var b = buffer[i]; + for (var j = skip; j < 8; ++j) + s.push((b >> j) & 1 ? '1' : '0'); + skip = 0; + } + object = s.reverse().join(''); + } + break; + case 0x04: // OCTET_STRING + object = new Uint8Array(buffer).buffer; + break; + // case 0x05: // NULL + case 0x06: // OBJECT_IDENTIFIER + var s = '', + n = 0, + bits = 0; + for (var i = 0; i < len; ++i) { + var v = buffer[i]; + n = (n << 7) + (v & 0x7F); + bits += 7; + if (!(v & 0x80)) { // finished + if (s === '') { + var m = n < 80 ? n < 40 ? 0 : 1 : 2; + s = m + "." + (n - m * 40); + } else + s += "." + n.toString(); + n = 0; + bits = 0; + } + } + if (bits > 0) + throw new DataError('Incompleted OID at offset ' + start); + object = s; + break; + //case 0x07: // ObjectDescriptor + //case 0x08: // EXTERNAL + //case 0x09: // REAL + //case 0x0A: // ENUMERATED + //case 0x0B: // EMBEDDED_PDV + case 0x10: // SEQUENCE + case 0x11: // SET + object = []; + break; + case 0x0C: // UTF8String + object = Chars.encode(buffer, 'utf8'); + break; + case 0x12: // NumericString + case 0x13: // PrintableString + case 0x14: // TeletexString + case 0x15: // VideotexString + case 0x16: // IA5String + case 0x19: // GraphicString + case 0x1A: // VisibleString + case 0x1B: // GeneralString + object = Chars.encode(buffer, 'ascii'); + break; + case 0x1C: // UniversalString + object = Chars.encode(buffer, 'utf32'); + break; + case 0x1E: // BMPString + object = Chars.encode(buffer, 'utf16'); + break; + case 0x17: // UTCTime + case 0x18: // GeneralizedTime + var shortYear = tagNumber === 0x17; + var s = Chars.encode(buffer, 'ascii'), + m = (shortYear ? + /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/ : + /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/).exec(s); + if (!m) + throw new DataError('Unrecognized time format "' + s + '" at offset ' + start); + if (shortYear) { + // Where YY is greater than or equal to 50, the year SHALL be interpreted as 19YY; and + // Where YY is less than 50, the year SHALL be interpreted as 20YY + m[1] = +m[1]; + m[1] += (m[1] < 50) ? 2000 : 1900; + } + var dt = new Date(m[1], +m[2] - 1, +m[3], +(m[4] || '0'), +(m[5] || '0'), +(m[6] || '0'), +(m[7] || '0')), + tz = dt.getTimezoneOffset(); + if (m[8] || tagNumber === 0x17) { + if (m[8].toUpperCase() !== 'Z' && m[9]) { + tz = tz + parseInt(m[9]); + } + dt.setMinutes(dt.getMinutes() - tz); + } + dt.original = s; + object = dt; + break; + } + } else // OCTET_STRING + object = new Uint8Array(buffer).buffer; + } else + object = sub; + + // result + return { + tagConstructed: tagConstructed, + tagClass: tagClass, + tagNumber: tagNumber, + header: header, + content: content, + object: object + }; + } + + return { + /** + * BER.decode(object, format) convert javascript object to ASN.1 format CryptoOperationData

+ * If object has members tagNumber, tagClass and tagConstructed + * it is clear define encoding rules. Else method use defaul rules: + *
    + *
  • Empty string or null - NULL
  • + *
  • String starts with '0x' and has 0-9 and a-f characters - INTEGER
  • + *
  • String like d.d.d.d (d - set of digits) - OBJECT IDENTIFIER
  • + *
  • String with characters 0 and 1 - BIT STRING
  • + *
  • Strings 'true' or 'false' - BOOLEAN
  • + *
  • String has only 0-9 and a-f characters - OCTET STRING
  • + *
  • String has only characters with code 0-255 - PrintableString
  • + *
  • Other strings - UTF8String
  • + *
  • Number - INTEGER
  • + *
  • Date - GeneralizedTime
  • + *
  • Boolean - SEQUENCE
  • + *
  • CryptoOperationData - OCTET STRING
  • + *
+ * SEQUENCE or SET arrays recursively encoded for each item.
+ * OCTET STRING and BIT STRING can presents as array with one item. + * It means encapsulates encoding for child element.
+ * + * If CONTEXT or APPLICATION classes item presents as array with one + * item we use EXPLICIT encoding for element, else IMPLICIT encoding.
+ * + * @memberOf GostCoding.BER + * @param {Object} object Object to encoding + * @param {string} format Encoding rule: 'DER' or 'CER', default 'DER' + * @param {boolean} onlyContent Encode content only, without header + * @returns {CryptoOperationData} BER encoded data + */ + encode: function (object, format, onlyContent) { + return encodeBER(object, format, onlyContent).buffer; + }, + /** + * BER.encode(data) convert ASN.1 format CryptoOperationData data to javascript object

+ * + * Conversion rules to javascript object: + *
    + *
  • BOOLEAN - Boolean object
  • + *
  • INTEGER, ENUMIRATED - Integer object if len <= 6 (48 bits) else Int16 encoded string
  • + *
  • BIT STRING - Integer object if len <= 5 (w/o unsedBit octet - 32 bits) else String like '10111100' or Array with one item in case of incapsulates encoding
  • + *
  • OCTET STRING - Hex encoded string or Array with one item in case of incapsulates encoding
  • + *
  • OBJECT IDENTIFIER - String with object identifier
  • + *
  • SEQUENCE, SET - Array of encoded items
  • + *
  • UTF8String, NumericString, PrintableString, TeletexString, VideotexString, + * IA5String, GraphicString, VisibleString, GeneralString, UniversalString, + * BMPString - encoded String
  • + *
  • UTCTime, GeneralizedTime - Date
  • + *
+ * @memberOf GostCoding.BER + * @param {(CryptoOperationData|GostCoding.BER)} data Binary data to decode + * @returns {Object} Javascript object with result of decoding + */ + decode: function (data) { + return decodeBER(data.object ? data : new Uint8Array(buffer(data)), 0); + } + }; //
+})(); + +/** + * BER, DER, CER conversion + * @memberOf GostCoding + * @insnance + * @type GostCoding.BER + */ +GostCoding.prototype.BER = BER; + +/** + * PEM conversion + * @class GostCoding.PEM + */ +var PEM = {// + /** + * PEM.encode(data, name) encode CryptoOperationData to PEM format with name label + * + * @memberOf GostCoding.PEM + * @param {(Object|CryptoOperationData)} data Java script object or BER-encoded binary data + * @param {string} name Name of PEM object: 'certificate', 'private key' etc. + * @returns {string} Encoded object + */ + encode: function (data, name) { + return (name ? '-----BEGIN ' + name.toUpperCase() + '-----\r\n' : '') + + Base64.encode(data instanceof CryptoOperationData ? data : BER.encode(data)) + + (name ? '\r\n-----END ' + name.toUpperCase() + '-----' : ''); + }, + /** + * PEM.decode(s, name, deep) decode PEM format s labeled name to CryptoOperationData or javascript object in according to deep parameter + * + * @memberOf GostCoding.PEM + * @param {string} s PEM encoded string + * @param {string} name Name of PEM object: 'certificate', 'private key' etc. + * @param {boolean} deep If true method do BER-decoding, else only BASE64 decoding + * @param {integer} index Index of decoded value + * @returns {(Object|CryptoOperationData)} Decoded javascript object if deep=true, else CryptoOperationData for father BER decoding + */ + decode: function (s, name, deep, index) { + // Try clear base64 + var re1 = /([A-Za-z0-9\+\/\s\=]+)/g, + valid = re1.exec(s); + if (valid[1].length !== s.length) + valid = false; + if (!valid && name) { + // Try with the name + var re2 = new RegExp( + '-----\\s?BEGIN ' + name.toUpperCase() + + '-----([A-Za-z0-9\\+\\/\\s\\=]+)-----\\s?END ' + + name.toUpperCase() + '-----', 'g'); + valid = re2.exec(s); + } + if (!valid) { + // Try with some name + var re3 = new RegExp( + '-----\\s?BEGIN [A-Z0-9\\s]+' + + '-----([A-Za-z0-9\\+\\/\\s\\=]+)-----\\s?END ' + + '[A-Z0-9\\s]+-----', 'g'); + valid = re3.exec(s); + } + var r = valid && valid[1 + (index || 0)]; + if (!r) + throw new DataError('Not valid PEM format'); + var out = Base64.decode(r); + if (deep) + out = BER.decode(out); + return out; + } // +}; + +/** + * PEM conversion + * @memberOf GostCoding + * @insnance + * @type GostCoding.PEM + */ +GostCoding.prototype.PEM = PEM; + +if (gostCrypto) + /** + * Coding algorithms: Base64, Hex, Int16, Chars, BER and PEM + * + * @memberOf gostCrypto + * @type GostCoding + */ + gostCrypto.coding = new GostCoding(); + +export default GostCoding; \ No newline at end of file diff --git a/src/core/vendor/gost/gostCrypto.mjs b/src/core/vendor/gost/gostCrypto.mjs new file mode 100644 index 00000000..daf0cf97 --- /dev/null +++ b/src/core/vendor/gost/gostCrypto.mjs @@ -0,0 +1,1653 @@ +/** + * Implementation Web Crypto interfaces for GOST algorithms + * 1.76 + * 2014-2016, Rudolf Nickolaev. All rights reserved. + * + * Exported for CyberChef by mshwed [m@ttshwed.com] + */ + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +import GostRandom from './gostRandom.mjs'; +import gostEngine from './gostEngine.mjs'; + +import crypto from 'crypto' + +/* +* Algorithm normalization +* +*/ // + +var root = {}; +root.gostEngine = gostEngine; + +var rootCrypto = crypto + +var SyntaxError = Error, + DataError = Error, + NotSupportedError = Error, + OperationError = Error, + InvalidStateError = Error, + InvalidAccessError = Error; + +// Normalize algorithm +function normalize(algorithm, method) { + if (typeof algorithm === 'string' || algorithm instanceof String) + algorithm = {name: algorithm}; + var name = algorithm.name; + if (!name) + throw new SyntaxError('Algorithm name not defined'); + // Extract algorithm modes from name + var modes = name.split('/'), modes = modes[0].split('-').concat(modes.slice(1)); + // Normalize the name with default modes + var na = {}; + name = modes[0].replace(/[\.\s]/g, ''); + modes = modes.slice(1); + if (name.indexOf('28147') >= 0) { + na = { + name: 'GOST 28147', + version: 1989, + mode: (algorithm.mode || (// ES, MAC, KW + (method === 'sign' || method === 'verify') ? 'MAC' : + (method === 'wrapKey' || method === 'unwrapKey') ? 'KW' : 'ES')).toUpperCase(), + length: algorithm.length || 64 + }; + } else if (name.indexOf('3412') >= 0) { + na = { + name: 'GOST R 34.12', + version: 2015, + mode: (algorithm.mode || (// ES, MAC, KW + (method === 'sign' || method === 'verify') ? 'MAC' : + (method === 'wrapKey' || method === 'unwrapKey') ? 'KW' : 'ES')).toUpperCase(), + length: algorithm.length || 64 // 128 + }; + } else if (name.indexOf('3411') >= 0) { + na = { + name: 'GOST R 34.11', + version: 2012, // 1994 + mode: (algorithm.mode || (// HASH, KDF, HMAC, PBKDF2, PFXKDF, CPKDF + (method === 'deriveKey' || method === 'deriveBits') ? 'KDF' : + (method === 'sign' || method === 'verify') ? 'HMAC' : 'HASH')).toUpperCase(), + length: algorithm.length || 256 // 512 + }; + } else if (name.indexOf('3410') >= 0) { + na = { + name: 'GOST R 34.10', + version: 2012, // 1994, 2001 + mode: (algorithm.mode || (// SIGN, DH, MASK + (method === 'deriveKey' || method === 'deriveBits') ? 'DH' : 'SIGN')).toUpperCase(), + length: algorithm.length || 256 // 512 + }; + } else if (name.indexOf('SHA') >= 0) { + na = { + name: 'SHA', + version: (algorithm.length || 160) === 160 ? 1 : 2, // 1, 2 + mode: (algorithm.mode || (// HASH, KDF, HMAC, PBKDF2, PFXKDF + (method === 'deriveKey' || method === 'deriveBits') ? 'KDF' : + (method === 'sign' || method === 'verify') ? 'HMAC' : 'HASH')).toUpperCase(), + length: algorithm.length || 160 + }; + } else if (name.indexOf('RC2') >= 0) { + na = { + name: 'RC2', + version: 1, + mode: (algorithm.mode || (// ES, MAC, KW + (method === 'sign' || method === 'verify') ? 'MAC' : + (method === 'wrapKey' || method === 'unwrapKey') ? 'KW' : 'ES')).toUpperCase(), + length: algorithm.length || 32 // 1 - 1024 + }; + } else if (name.indexOf('PBKDF2') >= 0) { + na = normalize(algorithm.hash, 'digest'); + na.mode = 'PBKDF2'; + } else if (name.indexOf('PFXKDF') >= 0) { + na = normalize(algorithm.hash, 'digest'); + na.mode = 'PFXKDF'; + } else if (name.indexOf('CPKDF') >= 0) { + na = normalize(algorithm.hash, 'digest'); + na.mode = 'CPKDF'; + } else if (name.indexOf('HMAC') >= 0) { + na = normalize(algorithm.hash, 'digest'); + na.mode = 'HMAC'; + } else + throw new NotSupportedError('Algorithm not supported'); + + // Compile modes + modes.forEach(function (mode) { + mode = mode.toUpperCase(); + if (/^[0-9]+$/.test(mode)) { + if ((['8', '16', '32'].indexOf(mode) >= 0) || (na.length === '128' && mode === '64')) { // Shift bits + if (na.mode === 'ES') + na.shiftBits = parseInt(mode); + else if (na.mode === 'MAC') + na.macLength = parseInt(mode); + else + throw new NotSupportedError('Algorithm ' + na.name + ' mode ' + mode + ' not supported'); + } else if (['89', '94', '01', '12', '15', '1989', '1994', '2001', '2012', '2015'].indexOf(mode) >= 0) { // GOST Year + var version = parseInt(mode); + version = version < 1900 ? (version < 80 ? 2000 + version : 1900 + version) : version; + na.version = version; + } else if (['1'].indexOf(mode) >= 0 && na.name === 'SHA') { // SHA-1 + na.version = 1; + na.length = 160; + } else if (['256', '384', '512'].indexOf(mode) >= 0 && na.name === 'SHA') { // SHA-2 + na.version = 2; + na.length = parseInt(mode); + } else if (['40', '128'].indexOf(mode) >= 0 && na.name === 'RC2') { // RC2 + na.version = 1; + na.length = parseInt(mode); // key size + } else if (['64', '128', '256', '512'].indexOf(mode) >= 0) // block size + na.length = parseInt(mode); + else if (['1000', '2000'].indexOf(mode) >= 0) // Iterations + na.iterations = parseInt(mode); + // Named Paramsets + } else if (['E-TEST', 'E-A', 'E-B', 'E-C', 'E-D', 'E-SC', 'E-Z', 'D-TEST', 'D-A', 'D-SC'].indexOf(mode) >= 0) { + na.sBox = mode; + } else if (['S-TEST', 'S-A', 'S-B', 'S-C', 'S-D', 'X-A', 'X-B', 'X-C'].indexOf(mode) >= 0) { + na.namedParam = mode; + } else if (['S-256-TEST', 'S-256-A', 'S-256-B', 'S-256-C', 'P-256', 'T-512-TEST', 'T-512-A', + 'T-512-B', 'X-256-A', 'X-256-B', 'T-256-TEST', 'T-256-A', 'T-256-B', 'S-256-B', 'T-256-C', 'S-256-C'].indexOf(mode) >= 0) { + na.namedCurve = mode; + } else if (['SC', 'CP', 'VN'].indexOf(mode) >= 0) { + na.procreator = mode; + + // Encription GOST 28147 or GOST R 34.12 + } else if (na.name === 'GOST 28147' || na.name === 'GOST R 34.12' || na.name === 'RC2') { + if (['ES', 'MAC', 'KW', 'MASK'].indexOf(mode) >= 0) { + na.mode = mode; + } else if (['ECB', 'CFB', 'OFB', 'CTR', 'CBC'].indexOf(mode) >= 0) { + na.mode = 'ES'; + na.block = mode; + } else if (['CPKW', 'NOKW', 'SCKW'].indexOf(mode) >= 0) { + na.mode = 'KW'; + na.keyWrapping = mode.replace('KW', ''); + } else if (['ZEROPADDING', 'PKCS5PADDING', 'NOPADDING', 'RANDOMPADDING', 'BITPADDING'].indexOf(mode) >= 0) { + na.padding = mode.replace('PADDING', ''); + } else if (['NOKM', 'CPKM'].indexOf(mode) >= 0) { + na.keyMeshing = mode.replace('KM', ''); + } else + throw new NotSupportedError('Algorithm ' + na.name + ' mode ' + mode + ' not supported'); + + // Digesting GOST 34.11 + } else if (na.name === 'GOST R 34.11' || na.name === 'SHA') { + if (['HASH', 'KDF', 'HMAC', 'PBKDF2', 'PFXKDF', 'CPKDF'].indexOf(mode) >= 0) + na.mode = mode; + else + throw new NotSupportedError('Algorithm ' + na.name + ' mode ' + mode + ' not supported'); + + // Signing GOST 34.10 + } else if (na.name === 'GOST R 34.10') { + var hash = mode.replace(/[\.\s]/g, ''); + if (hash.indexOf('GOST') >= 0 && hash.indexOf('3411') >= 0) + na.hash = mode; + else if (['SIGN', 'DH', 'MASK'].indexOf(mode)) + na.mode = mode; + else + throw new NotSupportedError('Algorithm ' + na.name + ' mode ' + mode + ' not supported'); + } + }); + + // Procreator + na.procreator = algorithm.procreator || na.procreator || 'CP'; + + // Key size + switch (na.name) { + case 'GOST R 34.10': + na.keySize = na.length / (na.version === 1994 ? 4 : 8); + break; + case 'GOST R 34.11': + na.keySize = 32; + break; + case 'GOST 28147': + case 'GOST R 34.12': + na.keySize = 32; + break; + case 'RC2': + na.keySize = Math.ceil(na.length / 8); + break; + case 'SHA': + na.keySize = na.length / 8; + break; + } + + // Encrypt additional modes + if (na.mode === 'ES') { + if (algorithm.block) + na.block = algorithm.block; // ECB, CFB, OFB, CTR, CBC + if (na.block) + na.block = na.block.toUpperCase(); + if (algorithm.padding) + na.padding = algorithm.padding; // NO, ZERO, PKCS5, RANDOM, BIT + if (na.padding) + na.padding = na.padding.toUpperCase(); + if (algorithm.shiftBits) + na.shiftBits = algorithm.shiftBits; // 8, 16, 32, 64 + if (algorithm.keyMeshing) + na.keyMeshing = algorithm.keyMeshing; // NO, CP + if (na.keyMeshing) + na.keyMeshing = na.keyMeshing.toUpperCase(); + // Default values + if (method !== 'importKey' && method !== 'generateKey') { + na.block = na.block || 'ECB'; + na.padding = na.padding || (na.block === 'CBC' || na.block === 'ECB' ? 'ZERO' : 'NO'); + if (na.block === 'CFB' || na.block === 'OFB') + na.shiftBits = na.shiftBits || na.length; + na.keyMeshing = na.keyMeshing || 'NO'; + } + } + if (na.mode === 'KW') { + if (algorithm.keyWrapping) + na.keyWrapping = algorithm.keyWrapping; // NO, CP, SC + if (na.keyWrapping) + na.keyWrapping = na.keyWrapping.toUpperCase(); + if (method !== 'importKey' && method !== 'generateKey') + na.keyWrapping = na.keyWrapping || 'NO'; + } + + // Paramsets + ['sBox', 'namedParam', 'namedCurve', 'curve', 'param', 'modulusLength'].forEach(function (name) { + algorithm[name] && (na[name] = algorithm[name]); + }); + // Default values + if (method !== 'importKey' && method !== 'generateKey') { + if (na.name === 'GOST 28147') { + na.sBox = na.sBox || (na.procreator === 'SC' ? 'E-SC' : 'E-A'); // 'E-A', 'E-B', 'E-C', 'E-D', 'E-SC' + } else if (na.name === 'GOST R 34.12' && na.length === 64) { + na.sBox = 'E-Z'; + } else if (na.name === 'GOST R 34.11' && na.version === 1994) { + na.sBox = na.sBox || (na.procreator === 'SC' ? 'D-SC' : 'D-A'); // 'D-SC' + } else if (na.name === 'GOST R 34.10' && na.version === 1994) { + na.namedParam = na.namedParam || (na.mode === 'DH' ? 'X-A' : 'S-A'); // 'S-B', 'S-C', 'S-D', 'X-B', 'X-C' + } else if (na.name === 'GOST R 34.10' && na.version === 2001) { + na.namedCurve = na.namedCurve || (na.length === 256 ? + na.procreator === 'SC' ? 'P-256' : (na.mode === 'DH' ? 'X-256-A' : 'S-256-A') : // 'S-256-B', 'S-256-C', 'X-256-B', 'T-256-A', 'T-256-B', 'T-256-C', 'P-256' + na.mode === 'T-512-A'); // 'T-512-B', 'T-512-C' + } else if (na.name === 'GOST R 34.10' && na.version === 2012) { + na.namedCurve = na.namedCurve || (na.length === 256 ? + na.procreator === 'SC' ? 'P-256' : (na.mode === 'DH' ? 'X-256-A' : 'S-256-A') : // 'S-256-B', 'S-256-C', 'X-256-B', 'T-256-A', 'T-256-B', 'T-256-C', 'P-256' + na.mode === 'T-512-A'); // 'T-512-B', 'T-512-C' + } + } + + // Vectors + switch (na.mode) { + case 'DH': + algorithm.ukm && (na.ukm = algorithm.ukm); + algorithm['public'] && (na['public'] = algorithm['public']); + break; + case 'SIGN': + case 'KW': + algorithm.ukm && (na.ukm = algorithm.ukm); + break; + case 'ES': + case 'MAC': + algorithm.iv && (na.iv = algorithm.iv); + break; + case 'KDF': + algorithm.label && (na.label = algorithm.label); + algorithm.contex && (na.context = algorithm.contex); + break; + case 'PBKDF2': + algorithm.salt && (na.salt = algorithm.salt); + algorithm.iterations && (na.iterations = algorithm.iterations); + algorithm.diversifier && (na.diversifier = algorithm.diversifier); + break; + case 'PFXKDF': + algorithm.salt && (na.salt = algorithm.salt); + algorithm.iterations && (na.iterations = algorithm.iterations); + algorithm.diversifier && (na.diversifier = algorithm.diversifier); + break; + case 'CPKDF': + algorithm.salt && (na.salt = algorithm.salt); + algorithm.iterations && (na.iterations = algorithm.iterations); + break; + } + + // Verification method and modes + if (method && ( + ((na.mode !== 'ES' && na.mode !== 'SIGN' && na.mode !== 'MAC' && + na.mode !== 'HMAC' && na.mode !== 'KW' && na.mode !== 'DH' + && na.mode !== 'MASK') && + (method === 'generateKey')) || + ((na.mode !== 'ES') && + (method === 'encrypt' || method === 'decrypt')) || + ((na.mode !== 'SIGN' && na.mode !== 'MAC' && na.mode !== 'HMAC') && + (method === 'sign' || method === 'verify')) || + ((na.mode !== 'HASH') && + (method === 'digest')) || + ((na.mode !== 'KW' && na.mode !== 'MASK') && + (method === 'wrapKey' || method === 'unwrapKey')) || + ((na.mode !== 'DH' && na.mode !== 'PBKDF2' && na.mode !== 'PFXKDF' && + na.mode !== 'CPKDF' && na.mode !== 'KDF') && + (method === 'deriveKey' || method === 'deriveBits')))) + throw new NotSupportedError('Algorithm mode ' + na.mode + ' not valid for method ' + method); + + // Normalize hash algorithm + algorithm.hash && (na.hash = algorithm.hash); + if (na.hash) { + if ((typeof na.hash === 'string' || na.hash instanceof String) + && na.procreator) + na.hash = na.hash + '/' + na.procreator; + na.hash = normalize(na.hash, 'digest'); + } + + // Algorithm object identirifer + algorithm.id && (na.id = algorithm.id); + + return na; +} + +// Check for possibility use native crypto.subtle +function checkNative(algorithm) { + if (!rootCrypto || !rootCrypto.subtle || !algorithm) + return false; + // Prepare name + var name = (typeof algorithm === 'string' || algorithm instanceof String) ? + name = algorithm : algorithm.name; + if (!name) + return false; + name = name.toUpperCase(); + // Digest algorithm for key derivation + if ((name.indexOf('KDF') >= 0 || name.indexOf('HMAC') >= 0) && algorithm.hash) + return checkNative(algorithm.hash); + // True if no supported names + return name.indexOf('GOST') === -1 && + name.indexOf('SHA-1') === -1 && + name.indexOf('RC2') === -1 && + name.indexOf('?DES') === -1; +} +// + +/* + * Key conversion methods + * + */ // + +// Check key parameter +function checkKey(key, method) { + if (!key.algorithm) + throw new SyntaxError('Key algorithm not defined'); + + if (!key.algorithm.name) + throw new SyntaxError('Key algorithm name not defined'); + + var name = key.algorithm.name, + gostCipher = name === 'GOST 28147' || name === 'GOST R 34.12' || name === 'RC2', + gostDigest = name === 'GOST R 34.11' || name === 'SHA', + gostSign = name === 'GOST R 34.10'; + + if (!gostCipher && !gostSign && !gostDigest) + throw new NotSupportedError('Key algorithm ' + name + ' is unsupproted'); + + if (!key.type) + throw new SyntaxError('Key type not defined'); + + if (((gostCipher || gostDigest) && key.type !== 'secret') || + (gostSign && !(key.type === 'public' || key.type === 'private'))) + throw new DataError('Key type ' + key.type + ' is not valid for algorithm ' + name); + + if (!key.usages || !key.usages.indexOf) + throw new SyntaxError('Key usages not defined'); + + for (var i = 0, n = key.usages.length; i < n; i++) { + var md = key.usages[i]; + if (((md === 'encrypt' || md === 'decrypt') && key.type !== 'secret') || + (md === 'sign' && key.type === 'public') || + (md === 'verify' && key.type === 'private')) + throw new InvalidStateError('Key type ' + key.type + ' is not valid for ' + md); + } + + if (method) + if (key.usages.indexOf(method) === -1) + throw new InvalidAccessError('Key usages is not contain method ' + method); + + if (!key.buffer) + throw new SyntaxError('Key buffer is not defined'); + + var size = key.buffer.byteLength * 8, keySize = 8 * key.algorithm.keySize; + if ((key.type === 'secret' && size !== (keySize || 256) && + (key.usages.indexOf('encrypt') >= 0 || key.usages.indexOf('decrypt') >= 0)) || + (key.type === 'private' && !(size === 256 || size === 512)) || + (key.type === 'public' && !(size === 512 || size === 1024))) + throw new SyntaxError('Key buffer has wrong size ' + size + ' bit'); +} + +// Extract key and enrich cipher algorithm +function extractKey(method, algorithm, key) { + checkKey(key, method); + if (algorithm) { + var params; + switch (algorithm.mode) { + case 'ES': + params = ['sBox', 'keyMeshing', 'padding', 'block']; + break; + case 'SIGN': + params = ['namedCurve', 'namedParam', 'sBox', 'curve', 'param', 'modulusLength']; + break; + case 'MAC': + params = ['sBox']; + break; + case 'KW': + params = ['keyWrapping', 'ukm']; + break; + case 'DH': + params = ['namedCurve', 'namedParam', 'sBox', 'ukm', 'curve', 'param', 'modulusLength']; + break; + case 'KDF': + params = ['context', 'label']; + break; + case 'PBKDF2': + params = ['sBox', 'iterations', 'salt']; + break; + case 'PFXKDF': + params = ['sBox', 'iterations', 'salt', 'diversifier']; + break; + case 'CPKDF': + params = ['sBox', 'salt']; + break; + } + if (params) + params.forEach(function (name) { + key.algorithm[name] && (algorithm[name] = key.algorithm[name]); + }); + } + return key.buffer; +} + +// Make key definition +function convertKey(algorithm, extractable, keyUsages, keyData, keyType) { + var key = { + type: keyType || (algorithm.name === 'GOST R 34.10' ? 'private' : 'secret'), + extractable: extractable || 'false', + algorithm: algorithm, + usages: keyUsages || [], + buffer: keyData + }; + checkKey(key); + return key; +} + +function convertKeyPair(publicAlgorithm, privateAlgorithm, extractable, keyUsages, publicBuffer, privateBuffer) { + + if (!keyUsages || !keyUsages.indexOf) + throw new SyntaxError('Key usages not defined'); + + var publicUsages = keyUsages.filter(function (value) { + return value !== 'sign'; + }); + var privateUsages = keyUsages.filter(function (value) { + return value !== 'verify'; + }); + + return { + publicKey: convertKey(publicAlgorithm, extractable, publicUsages, publicBuffer, 'public'), + privateKey: convertKey(privateAlgorithm, extractable, privateUsages, privateBuffer, 'private') + }; +} + +// Swap bytes in buffer +function swapBytes(src) { + if (src instanceof CryptoOperationData) + src = new Uint8Array(src); + var dst = new Uint8Array(src.length); + for (var i = 0, n = src.length; i < n; i++) + dst[n - i - 1] = src[i]; + return dst.buffer; +} +// + +/** + * Promise stub object (not fulfill specification, only for internal use) + * Class not defined if Promise class already defined in root context

+ * + * The Promise object is used for deferred and asynchronous computations. A Promise is in one of the three states: + *
    + *
  • pending: initial state, not fulfilled or rejected.
  • + *
  • fulfilled: successful operation
  • + *
  • rejected: failed operation.
  • + *
+ * Another term describing the state is settled: the Promise is either fulfilled or rejected, but not pending.

+ * @class Promise + * @global + * @param {function} executor Function object with two arguments resolve and reject. + * The first argument fulfills the promise, the second argument rejects it. + * We can call these functions, once our operation is completed. + */ // +if (!Promise) { + + root.Promise = (function () { + + function mswrap(value) { + if (value && value.oncomplete === null && value.onerror === null) { + return new Promise(function (resolve, reject) { + value.oncomplete = function () { + resolve(value.result); + }; + value.onerror = function () { + reject(new OperationError(value.toString())); + }; + }); + } else + return value; + } + + function Promise(executor) { + + var state = 'pending', result, + resolveQueue = [], rejectQueue = []; + + function call(callback) { + try { + callback(); + } catch (e) { + } + } + + try { + executor(function (value) { + if (state === 'pending') { + state = 'fulfilled'; + result = value; + resolveQueue.forEach(call); + } + }, function (reason) { + if (state === 'pending') { + state = 'rejected'; + result = reason; + rejectQueue.forEach(call); + } + }); + } catch (error) { + if (state === 'pending') { + state = 'rejected'; + result = error; + rejectQueue.forEach(call); + } + } + /** + * The then() method returns a Promise. It takes two arguments, both are + * callback functions for the success and failure cases of the Promise. + * + * @method then + * @memberOf Promise + * @instance + * @param {function} onFulfilled A Function called when the Promise is fulfilled. This function has one argument, the fulfillment value. + * @param {function} onRejected A Function called when the Promise is rejected. This function has one argument, the rejection reason. + * @returns {Promise} + */ + this.then = function (onFulfilled, onRejected) { + + return new Promise(function (resolve, reject) { + + function asyncOnFulfilled() { + var value; + try { + value = onFulfilled ? onFulfilled(result) : result; + } catch (error) { + reject(error); + return; + } + value = mswrap(value); + if (value && value.then && value.then.call) { + value.then(resolve, reject); + } else { + resolve(value); + } + } + + function asyncOnRejected() { + var reason; + try { + reason = onRejected ? onRejected(result) : result; + } catch (error) { + reject(error); + return; + } + reason = mswrap(reason); + if (reason && reason.then && reason.then.call) { + reason.then(resolve, reject); + } else { + reject(reason); + } + } + + if (state === 'fulfilled') { + asyncOnFulfilled(); + } else if (state === 'rejected') { + asyncOnRejected(); + } else { + resolveQueue.push(asyncOnFulfilled); + rejectQueue.push(asyncOnRejected); + } + + }); + + }; + /** + * The catch() method returns a Promise and deals with rejected cases only. + * It behaves the same as calling Promise.prototype.then(undefined, onRejected). + * + * @method catch + * @memberOf Promise + * @instance + * @param {function} onRejected A Function called when the Promise is rejected. This function has one argument, the rejection reason. + * @returns {Promise} + */ + this['catch'] = function (onRejected) { + return this.then(undefined, onRejected); + }; + } + + /** + * The Promise.all(iterable) method returns a promise that resolves when all + * of the promises in the iterable argument have resolved.

+ * + * The result is passed as an array of values from all the promises. + * If something passed in the iterable array is not a promise, it's converted to + * one by Promise.resolve. If any of the passed in promises rejects, the + * all Promise immediately rejects with the value of the promise that rejected, + * discarding all the other promises whether or not they have resolved. + * + * @method all + * @memberOf Promise + * @static + * @param {KeyUsages} promises Array with promises. + * @returns {Promise} + */ + Promise.all = function (promises) { + return new Promise(function (resolve, reject) { + var result = [], count = 0; + function asyncResolve(k) { + count++; + return function (data) { + result[k] = data; + count--; + if (count === 0) + resolve(result); + }; + } + + function asyncReject(reason) { + if (count > 0) + reject(reason); + count = 0; + } + + for (var i = 0, n = promises.length; i < n; i++) { + var data = promises[i]; + if (data.then && data.then.call) + data.then(asyncResolve(i), asyncReject); + else + result[i] = data; + } + + if (count === 0) + resolve(result); + }); + }; + + return Promise; + })(); +} //
+ +/* + * Worker executor + * + */ // + +var baseUrl = '', nameSuffix = ''; +// Try to define from DOM model +if (typeof document !== 'undefined') { + (function () { + var regs = /^(.*)gostCrypto(.*)\.js$/i; + var list = document.querySelectorAll('script'); + for (var i = 0, n = list.length; i < n; i++) { + var value = list[i].getAttribute('src'); + var test = regs.exec(value); + if (test) { + baseUrl = test[1]; + nameSuffix = test[2]; + } + } + })(); +} + +// Local importScripts procedure for include dependens +function importScripts() { + for (var i = 0, n = arguments.length; i < n; i++) { + var name = arguments[i].split('.'), + src = baseUrl + name[0] + nameSuffix + '.' + name[1]; + var el = document.querySelector('script[src="' + src + '"]'); + if (!el) { + el = document.createElement('script'); + el.setAttribute('src', src); + document.head.appendChild(el); + } + } +} + +// Create Worker +var worker = false, tasks = [], sequence = 0; +// Worker will create only for first child process and +// Gost implementation libraries not yet loaded +if (!root.importScripts && !root.gostEngine) { + + try { + worker = new Worker(baseUrl + 'gostEngine' + nameSuffix + '.js'); + + // Result of opertion + worker.onmessage = function (event) { + // Find task + var id = event.data.id; + for (var i = 0, n = tasks.length; i < n; i++) + if (tasks[i].id === id) + break; + if (i < n) { + var task = tasks[i]; + tasks.splice(i, 1); + // Reject if error or resolve with result + if (event.data.error) + task.reject(new OperationError(event.data.error)); + else + task.resolve(event.data.result); + } + }; + + // Worker error - reject all waiting tasks + worker.onerror = function (event) { + for (var i = 0, n = tasks.length; i < n; i++) + tasks[i].reject(event.error); + tasks = []; + }; + + } catch (e) { + // Worker is't supported + worker = false; + } +} + +if (!root.importScripts) { + // This procedure emulate load dependents as in Worker + root.importScripts = importScripts; + +} + +if (!worker) { + // Import main module + // Reason: we are already in worker process or Worker interface is not + // yet supported + root.gostEngine || require('./gostEngine'); +} + +// Executor for any method +function execute(algorithm, method, args) { + return new Promise(function (resolve, reject) { + try { + if (worker) { + var id = ++sequence; + tasks.push({ + id: id, + resolve: resolve, + reject: reject + }); + worker.postMessage({ + id: id, algorithm: algorithm, + method: method, args: args + }); + } else { + if (root.gostEngine) + resolve(root.gostEngine.execute(algorithm, method, args)); + else + reject(new OperationError('Module gostEngine not found')); + } + } catch (error) { + reject(error); + } + }); +} + +// Self resolver +function call(callback) { + try { + callback(); + } catch (e) { + } +} + +// + +/* + * WebCrypto common class references + * + */ // +/** + * The Algorithm object is a dictionary object [WebIDL] which is used to + * specify an algorithm and any additional parameters required to fully + * specify the desired operation.
+ *
+ *  dictionary Algorithm {
+ *      DOMString name;
+ *  };
+ * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#algorithm-dictionary} + * @class Algorithm + * @param {DOMString} name The name of the registered algorithm to use. + */ + +/** + * AlgorithmIdentifier - Algorithm or DOMString name of algorithm
+ *
+ *  typedef (Algorithm or DOMString) AlgorithmIdentifier;
+ * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#algorithm-dictionary} + * @class AlgorithmIdentifier + */ + +/** + * The KeyAlgorithm interface represents information about the contents of a + * given Key object. + *
+ *  interface KeyAlgorithm {
+ *      readonly attribute DOMString name
+ *  };
+ * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-algorithm-interface} + * @class KeyAlgorithm + * @param {DOMString} name The name of the algorithm used to generate the Key + */ + +/** + * The type of a key. The recognized key type values are "public", "private" + * and "secret". Opaque keying material, including that used for symmetric + * algorithms, is represented by "secret", while keys used as part of asymmetric + * algorithms composed of public/private keypairs will be either "public" or "private". + *
+ *  typedef DOMString KeyType;
+ * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-interface} + * @class KeyType + */ + +/** + * Sequence of operation type that may be performed using a key. The recognized + * key usage values are "encrypt", "decrypt", "sign", "verify", "deriveKey", + * "deriveBits", "wrapKey" and "unwrapKey". + *
+ *  typedef DOMString[] KeyUsages;
+ * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-interface} + * @class KeyUsages + */ + +/** + * The Key object represents an opaque reference to keying material that is + * managed by the user agent.
+ * This specification provides a uniform interface for many different kinds of + * keying material managed by the user agent. This may include keys that have + * been generated by the user agent, derived from other keys by the user agent, + * imported to the user agent through user actions or using this API, + * pre-provisioned within software or hardware to which the user agent has + * access or made available to the user agent in other ways. The term key refers + * broadly to any keying material including actual keys for cryptographic + * operations and secret values obtained within key derivation or exchange operations.
+ * The Key object is not required to directly interface with the underlying key + * storage mechanism, and may instead simply be a reference for the user agent + * to understand how to obtain the keying material when needed, eg. when performing + * a cryptographic operation. + *
+ *  interface Key {
+ *      readonly attribute KeyType type;
+ *      readonly attribute boolean extractable;
+ *      readonly attribute KeyAlgorithm algorithm;
+ *      readonly attribute KeyUsages usages;
+ *  };     
+ * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-interface} + * @class Key + * @param {KeyType} type The type of a key. The recognized key type values are "public", "private" and "secret". + * @param {boolean} extractable Whether or not the raw keying material may be exported by the application. + * @param {KeyAlgorithm} algorithm The Algorithm used to generate the key. + * @param {KeyUsages} usages Key usage array: type of operation that may be performed using a key. + */ + +/** + * The KeyPair interface represents an asymmetric key pair that is comprised of both public and private keys. + *
+ *  interface KeyPair {
+ *      readonly attribute Key publicKey;
+ *      readonly attribute Key privateKey;
+ *  };     
+ * 
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#keypair} + * @class KeyPair + * @param {Key} privateKey Private key + * @param {Key} publicKey Public key + */ + +/** + * Specifies a serialization format for a key. The recognized key format values are: + *
    + *
  • 'raw' - An unformatted sequence of bytes. Intended for secret keys.
  • + *
  • 'pkcs8' - The DER encoding of the PrivateKeyInfo structure from RFC 5208.
  • + *
  • 'spki' - The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.
  • + *
  • 'jwk' - The key is represented as JSON according to the JSON Web Key format.
  • + *
+ *
+ *  typedef DOMString KeyFormat;
+ *  
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#key-interface} + * @class KeyFormat + */ + +/** + * Binary data + *
+ *  typedef (ArrayBuffer or ArrayBufferView) CryptoOperationData;
+ *  
+ * @class CryptoOperationData + */ +var CryptoOperationData = ArrayBuffer; + +/** + * DER-encoded ArrayBuffer or PEM-encoded DOMString constains ASN.1 object
+ *
+ *  typedef (ArrayBuffer or DOMString) FormatedData;
+ * 
+ * @class FormatedData + */ +//
+ +/** + * The gostCrypto provide general purpose cryptographic functionality for + * GOST standards including a cryptographically strong pseudo-random number + * generator seeded with truly random values. + * + * @namespace gostCrypto + */ +var gostCrypto = {}; + +/** + * The SubtleCrypto class provides low-level cryptographic primitives and algorithms. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#subtlecrypto-interface} + * + * @class SubtleCrypto + */ // +function SubtleCrypto() { +} + +/** + * The encrypt method returns a new Promise object that will encrypt data + * using the specified algorithm identifier with the supplied Key. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-encrypt}

+ * + * Supported algorithm names: + *
    + *
  • GOST 28147-ECB "prostaya zamena" (ECB) mode (default)
  • + *
  • GOST 28147-CFB "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode
  • + *
  • GOST 28147-OFB "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode
  • + *
  • GOST 28147-CTR "gammirovanie" (counter) mode
  • + *
  • GOST 28147-CBC Cipher-Block-Chaining (CBC) mode
  • + *
  • GOST R 34.12-ECB "prostaya zamena" (ECB) mode (default)
  • + *
  • GOST R 34.12-CFB "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode
  • + *
  • GOST R 34.12-OFB "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode
  • + *
  • GOST R 34.12-CTR "gammirovanie" (counter) mode
  • + *
  • GOST R 34.12-CBC Cipher-Block-Chaining (CBC) mode
  • + *
+ * For more information see {@link GostCipher} + * + * @memberOf SubtleCrypto + * @method encrypt + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {Key} key Key object + * @param {CryptoOperationData} data Operation data + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ +SubtleCrypto.prototype.encrypt = function (algorithm, key, data) // +{ + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.encrypt(algorithm, key, data); + + algorithm = normalize(algorithm, 'encrypt'); + return execute(algorithm, 'encrypt', + [extractKey('encrypt', algorithm, key), data]); + }); +}; // + +/** + * The decrypt method returns a new Promise object that will decrypt data + * using the specified algorithm identifier with the supplied Key. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-decrypt}

+ * + * Supported algorithm names: + *
    + *
  • GOST 28147-ECB "prostaya zamena" (ECB) mode (default)
  • + *
  • GOST 28147-CFB "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode
  • + *
  • GOST 28147-OFB "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode
  • + *
  • GOST 28147-CTR "gammirovanie" (counter) mode
  • + *
  • GOST 28147-CBC Cipher-Block-Chaining (CBC) mode
  • + *
  • GOST R 34.12-ECB "prostaya zamena" (ECB) mode (default)
  • + *
  • GOST R 34.12-CFB "gammirovanie s obratnoj svyaziyu po shifrotekstu" (CFB) mode
  • + *
  • GOST R 34.12-OFB "gammirovanie s obratnoj svyaziyu po vyhodu" (OFB) mode
  • + *
  • GOST R 34.12-CTR "gammirovanie" (counter) mode
  • + *
  • GOST R 34.12-CBC Cipher-Block-Chaining (CBC) mode
  • + *
+ * For additional modes see {@link GostCipher} + * + * @memberOf SubtleCrypto + * @method decrypt + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {Key} key Key object + * @param {CryptoOperationData} data Operation data + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ +SubtleCrypto.prototype.decrypt = function (algorithm, key, data) // +{ + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.decrypt(algorithm, key, data); + + algorithm = normalize(algorithm, 'decrypt'); + return execute(algorithm, 'decrypt', + [extractKey('decrypt', algorithm, key), data]); + }); +}; // + +/** + * The sign method returns a new Promise object that will sign data using + * the specified algorithm identifier with the supplied Key. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-sign}

+ * + * Supported algorithm names: + *
    + *
  • GOST R 34.10-94 GOST Signature
  • + *
  • GOST R 34.10-94/GOST R 34.11-94 GOST Signature with Hash
  • + *
  • GOST R 34.10 ECGOST Signature
  • + *
  • GOST R 34.10/GOST R 34.11-94 ECGOST Signature with Old-Style Hash
  • + *
  • GOST R 34.10/GOST R 34.11 ECGOST Signature with Streebog Hash
  • + *
  • GOST 28147-MAC MAC base on GOST 28147
  • + *
  • GOST R 34.12-MAC MAC base on GOST R 43.12
  • + *
  • GOST R 34.11-HMAC HMAC base on GOST 34.11
  • + *
  • SHA-HMAC HMAC base on SHA
  • + *
+ * For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher} + * + * @memberOf SubtleCrypto + * @method sign + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {Key} key Key object + * @param {CryptoOperationData} data Operation data + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ +SubtleCrypto.prototype.sign = function (algorithm, key, data) // +{ + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.sign(algorithm, key, data); + + algorithm = normalize(algorithm, 'sign'); + var value = execute(algorithm, 'sign', + [extractKey('sign', algorithm, key), data]).then(function (data) { + if (algorithm.procreator === 'SC' && algorithm.mode === 'SIGN') { + data = gostCrypto.asn1.GostSignature.encode(data); + } + return data; + }); + return value; + }); +}; // + +/** + * The verify method returns a new Promise object that will verify data + * using the specified algorithm identifier with the supplied Key. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-verify}

+ * + * Supported algorithm names: + *
    + *
  • GOST R 34.10-94 GOST Signature
  • + *
  • GOST R 34.10-94/GOST R 34.11-94 GOST Signature with Hash
  • + *
  • GOST R 34.10 ECGOST Signature
  • + *
  • GOST R 34.10/GOST R 34.11-94 ECGOST Signature with Old-Style Hash
  • + *
  • GOST R 34.10/GOST R 34.11 ECGOST Signature with Streebog Hash
  • + *
  • GOST 28147-MAC MAC base on GOST 28147
  • + *
  • GOST R 34.12-MAC MAC base on GOST R 34.12
  • + *
  • GOST R 34.11-HMAC HMAC base on GOST 34.11
  • + *
  • SHA-HMAC HMAC base on SHA
  • + *
+ * For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher} + * + * @memberOf SubtleCrypto + * @method verify + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {Key} key Key object + * @param {CryptoOperationData} signature Signature data + * @param {CryptoOperationData} data Operation data + * @returns {Promise} Promise that resolves with boolean value of verification result + */ +SubtleCrypto.prototype.verify = function (algorithm, key, signature, data) // +{ + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.verify(algorithm, key, signature, data); + + algorithm = normalize(algorithm, 'verify'); + if (algorithm.procreator === 'SC' && algorithm.mode === 'SIGN') { + var obj = gostCrypto.asn1.GostSignature.decode(signature); + signature = {r: obj.r, s: obj.s}; + } + return execute(algorithm, 'verify', + [extractKey('verify', algorithm, key), signature, data]); + }); +}; // + +/** + * The digest method returns a new Promise object that will digest data + * using the specified algorithm identifier. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-digest}

+ * + * Supported algorithm names: + *
    + *
  • GOST R 34.11-94 Old-Style GOST Hash
  • + *
  • GOST R 34.11 GOST Streebog Hash
  • + *
  • SHA SHA Hash
  • + *
+ * For additional modes see {@link GostDigest} + * + * @memberOf SubtleCrypto + * @method digest + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {CryptoOperationData} data Operation data + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ +SubtleCrypto.prototype.digest = function (algorithm, data) // +{ + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.digest(algorithm, data); + + algorithm = normalize(algorithm, 'digest'); + return execute(algorithm, 'digest', [data]); + }); +}; // + +/** + * The generateKey method returns a new Promise object that will key(s) using + * the specified algorithm identifier. Key can be used in according with + * KeyUsages sequence. The recognized key usage values are "encrypt", "decrypt", + * "sign", "verify", "deriveKey", "deriveBits", "wrapKey" and "unwrapKey". + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-generateKey}

+ * + * Supported algorithm names: + *
    + *
  • GOST R 34.10 ECGOST Key Pairs
  • + *
  • GOST 28147 Key for encryption GOST 28147 modes
  • + *
  • GOST 28147-KW Key for wrapping GOST 28147 modes
  • + *
  • GOST R 34.12 Key for encryption GOST R 34.12 modes
  • + *
  • GOST R 34.12-KW Key for wrapping GOST R 34.12 modes
  • + *
  • GOST R 34.11-KDF Key for Derivation Algorithm
  • + *
+ * For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher}
+ * Note: Generation key for GOST R 34.10-94 not supported. + * + * @memberOf SubtleCrypto + * @method generateKey + * @instance + * @param {AlgorithmIdentifier} algorithm Key algorithm identifier + * @param {boolean} extractable Whether or not the raw keying material may be exported by the application + * @param {KeyUsages} keyUsages Key usage array: type of operation that may be performed using a key + * @returns {Promise} Promise that resolves with {@link Key} or {@link KeyPair} in according to key algorithm + */ +SubtleCrypto.prototype.generateKey = function (algorithm, extractable, keyUsages) // +{ + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.generateKey(algorithm, extractable, keyUsages); + + var privateAlgorithm = algorithm.privateKey, + publicAlgorithm = algorithm.publicKey; + algorithm = normalize(algorithm, 'generateKey'); + if (privateAlgorithm) + privateAlgorithm = normalize(privateAlgorithm, 'generateKey'); + else + privateAlgorithm = algorithm; + if (publicAlgorithm) + publicAlgorithm = normalize(publicAlgorithm, 'generateKey'); + else + publicAlgorithm = algorithm; + return execute(algorithm, 'generateKey', []).then(function (data) { + if (data.publicKey && data.privateKey) + return convertKeyPair(publicAlgorithm, privateAlgorithm, extractable, keyUsages, data.publicKey, data.privateKey); + else + return convertKey(algorithm, extractable, keyUsages, data); + }); + }); +}; // + +/** + * The deriveKey method returns a new Promise object that will key(s) using + * the specified algorithm identifier. Key can be used in according with + * KeyUsage sequence. The recognized key usage values are "encrypt", "decrypt", + * "sign", "verify", "deriveKey", "deriveBits", "wrapKey" and "unwrapKey". + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-deriveKey}

+ * + * Supported algorithm names: + *
    + *
  • GOST R 34.10-DH ECDH Key Agreement mode
  • + *
  • GOST R 34.11-KDF Key for Derivation Algorithm
  • + *
  • GOST R 34.11-PBKDF2 Password Based Key for Derivation Algorithm
  • + *
  • GOST R 34.11-PFXKDF PFX Key for Derivation Algorithm
  • + *
  • GOST R 34.11-CPKDF Password Based Key for CryptoPro Derivation Algorithm
  • + *
  • SHA-PBKDF2 Password Based Key for Derivation Algorithm
  • + *
  • SHA-PFXKDF PFX Key for Derivation Algorithm
  • + *
+ * For additional modes see {@link GostSign} and {@link GostDigest} + * + * @memberOf SubtleCrypto + * @method deriveKey + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {Key} baseKey Derivation key object + * @param {AlgorithmIdentifier} derivedKeyType Derived key algorithm identifier + * @param {boolean} extractable Whether or not the raw keying material may be exported by the application + * @param {KeyUsages} keyUsages Key usage array: type of operation that may be performed using a key + * @returns {Promise} Promise that resolves with {@link Key} + */ +SubtleCrypto.prototype.deriveKey = function (algorithm, baseKey, + derivedKeyType, extractable, keyUsages) // +{ + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.deriveKey(algorithm, baseKey, + derivedKeyType, extractable, keyUsages); + + algorithm = normalize(algorithm, 'deriveKey'); + derivedKeyType = normalize(derivedKeyType, 'generateKey'); + algorithm.keySize = derivedKeyType.keySize; + if (algorithm['public']) { + algorithm['public'].algorithm = normalize(algorithm['public'].algorithm); + algorithm['public'] = extractKey('deriveKey', algorithm, algorithm['public']); + } + return execute(algorithm, 'deriveKey', [extractKey('deriveKey', algorithm, baseKey)]).then(function (data) { + return convertKey(derivedKeyType, extractable, keyUsages, data); + }); + }); +}; // + +/** + * The deriveBits method returns length bits on baseKey using the + * specified algorithm identifier. + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-deriveBits}

+ * + * Supported algorithm names: + *
    + *
  • GOST R 34.10-DH ECDH Key Agreement mode
  • + *
  • GOST R 34.11-KDF Key for Derivation Algorithm
  • + *
  • GOST R 34.11-PBKDF2 Password Based Key for Derivation Algorithm
  • + *
  • GOST R 34.11-PFXKDF PFX Key for Derivation Algorithm
  • + *
  • GOST R 34.11-CPKDF Password Based Key for CryptoPro Derivation Algorithm
  • + *
  • SHA-PBKDF2 Password Based Key for Derivation Algorithm
  • + *
  • SHA-PFXKDF PFX Key for Derivation Algorithm
  • + *
+ * For additional modes see {@link GostSign} and {@link GostDigest} + * + * @memberOf SubtleCrypto + * @method deriveBits + * @instance + * @param {AlgorithmIdentifier} algorithm Algorithm identifier + * @param {Key} baseKey Derivation key object + * @param {number} length Length bits + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ +SubtleCrypto.prototype.deriveBits = function (algorithm, baseKey, length) // +{ + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.deriveBits(algorithm, baseKey, length); + + algorithm = normalize(algorithm, 'deriveBits'); + if (algorithm['public']) + algorithm['public'] = extractKey('deriveBits', algorithm, algorithm['public']); + return execute(algorithm, 'deriveBits', [extractKey('deriveBits', algorithm, baseKey), length]); + }); +}; // + +/** + * The importKey method returns a new Promise object that will key(s) using + * the specified algorithm identifier. Key can be used in according with + * KeyUsage sequence. The recognized key usage values are "encrypt", "decrypt", + * "sign", "verify", "deriveKey", "deriveBits", "wrapKey" and "unwrapKey".

+ * Parameter keyData contains data in defined format. + * The suppored key format values are: + *
    + *
  • 'raw' - An unformatted sequence of bytes. Intended for secret keys.
  • + *
  • 'pkcs8' - The DER encoding of the PrivateKeyInfo structure from RFC 5208.
  • + *
  • 'spki' - The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.
  • + *
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-importKey}

+ * + * Supported algorithm names: + *
    + *
  • GOST R 34.10-94 GOST Private and Public keys
  • + *
  • GOST R 34.10 ECGOST Private and Public keys
  • + *
  • GOST 28147 Key for encryption GOST 28147 modes
  • + *
  • GOST 28147-KW Key for key wrapping GOST 28147 modes
  • + *
  • GOST R 34.12 Key for encryption GOST 34.12 modes
  • + *
  • GOST R 34.12-KW Key for key wrapping GOST 34.12 modes
  • + *
  • GOST R 34.11-KDF Key for Derivation Algorithm
  • + *
+ * For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher}
+ * + * @memberOf SubtleCrypto + * @method importKey + * @instance + * @param {KeyFormat} format Key format Format specifies a serialization format for a key + * @param {CryptoOperationData} keyData + * @param {AlgorithmIdentifier} algorithm Key algorithm identifier + * @param {boolean} extractable Whether or not the raw keying material may be exported by the application + * @param {KeyUsages} keyUsages Key usage array: type of operation that may be performed using a key + * @returns {Promise} Promise that resolves with {@link Key} + */ +SubtleCrypto.prototype.importKey = function (format, keyData, algorithm, extractable, keyUsages) // +{ + var type; + return new Promise(call).then(function () { + if (checkNative(algorithm)) + return rootCrypto.subtle.importKey(format, keyData, algorithm, extractable, keyUsages); + + if (format === 'raw') { + algorithm = normalize(algorithm, 'importKey'); + if (keyUsages && keyUsages.indexOf) { + var name = algorithm.name.toUpperCase().replace(/[\.\s]/g, ''); + if (name.indexOf('3410') >= 0 && keyUsages.indexOf('sign') >= 0) + type = 'private'; + else if (name.indexOf('3410') >= 0 && keyUsages.indexOf('verify') >= 0) + type = 'public'; + } + return keyData; + } else { + var key; + if (format === 'pkcs8') + key = gostCrypto.asn1.GostPrivateKeyInfo.decode(keyData).object; + else if (format === 'spki') + key = gostCrypto.asn1.GostSubjectPublicKeyInfo.decode(keyData).object; + else + throw new NotSupportedError('Key format not supported'); + + algorithm = normalize(key.algorithm, 'importKey'); + type = key.type; + if (extractable !== false) + extractable = extractable || key.extractable; + if (keyUsages) { + for (var i = 0; i < keyUsages.length; i++) { + if (key.usages.indexOf(keyUsages[i]) < 0) + throw DataError('Key usage not valid for this key'); + } + } else + keyUsages = key.usages; + var data = key.buffer, keySize = algorithm.keySize, dataLen = data.byteLength; + if (type === 'public' || keySize === dataLen) + return data; + else { + // Remove private key masks + if (dataLen % keySize > 0) + throw new DataError('Invalid key size'); + algorithm.mode = 'MASK'; + algorithm.procreator = 'VN'; + var chain = []; + for (var i = keySize; i < dataLen; i += keySize) { + chain.push((function (mask) { + return function (data) { + return execute(algorithm, 'unwrapKey', [mask, data]).then(function (data) { + var next = chain.pop(); + if (next) + return next(data); + else { + delete algorithm.mode; + return data; + } + }); + }; + })(new Uint8Array(data, i, keySize))); + } + return chain.pop()(new Uint8Array(data, 0, keySize)); + } + } + }).then(function (data) { + return convertKey(algorithm, extractable, keyUsages, data, type); + }); +}; // + +/** + * The exportKey method returns a new Promise object that will key data in + * defined format.

+ * The suppored key format values are: + *
    + *
  • 'raw' - An unformatted sequence of bytes. Intended for secret keys.
  • + *
  • 'pkcs8' - The DER encoding of the PrivateKeyInfo structure from RFC 5208.
  • + *
  • 'spki' - The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.
  • + *
+ * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-exportKey}

+ * + * Supported algorithm names: + *
    + *
  • GOST R 34.10-94 GOST Private and Public keys
  • + *
  • GOST R 34.10 ECGOST Private and Public keys
  • + *
  • GOST 28147 Key for encryption GOST 28147 modes
  • + *
  • GOST 28147-KW Key for key wrapping GOST 28147 modes
  • + *
  • GOST R 34.12 Key for encryption GOST R 34.12 modes
  • + *
  • GOST R 34.12-KW Key for key wrapping GOST R 34.12 modes
  • + *
  • GOST R 34.11-KDF Key for Derivation Algorithm
  • + *
  • GOST R 34.11-PBKDF2 Import Password for Key for Derivation Algorithm
  • + *
  • GOST R 34.11-PFXKDF Import PFX Key for Derivation Algorithm
  • + *
  • GOST R 34.11-CPKDF Import Password Key for CryptoPro Derivation Algorithm
  • + *
  • SHA-PBKDF2 Import Password for Key for Derivation Algorithm
  • + *
  • SHA-PFXKDF Import PFX Key for Derivation Algorithm
  • + *
+ * For additional modes see {@link GostSign}, {@link GostDigest} and {@link GostCipher}
+ * + * @memberOf SubtleCrypto + * @method exportKey + * @instance + * @param {KeyFormat} format Format specifies a serialization format for a key + * @param {Key} key Key object + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ +SubtleCrypto.prototype.exportKey = function (format, key) // +{ + return new Promise(call).then(function () { + if (key && checkNative(key.algorithm)) + return rootCrypto.subtle.exportKey(format, key); + + if (!key.extractable) + throw new InvalidAccessError('Key not extractable'); + + var raw = extractKey(null, null, key); + if (format === 'raw') + return raw; + else if (format === 'pkcs8' && key.algorithm && key.algorithm.id) { + if (key.algorithm.procreator === 'VN') { + // Add masks for ViPNet + var algorithm = key.algorithm, mask; + algorithm.mode = 'MASK'; + return execute(algorithm, 'generateKey').then(function (data) { + mask = data; + return execute(algorithm, 'wrapKey', [mask, key.buffer]); + }).then(function (data) { + delete algorithm.mode; + var d = new Uint8Array(data.byteLength + mask.byteLength); + d.set(new Uint8Array(data, 0, data.byteLength)); + d.set(new Uint8Array(mask, 0, mask.byteLength), data.byteLength); + var buffer = d.buffer; + buffer.enclosed = true; + return gostCrypto.asn1.GostPrivateKeyInfo.encode({ + algorithm: algorithm, + buffer: buffer + }); + }); + } else + return gostCrypto.asn1.GostPrivateKeyInfo.encode(key); + } else if (format === 'spki' && key.algorithm && key.algorithm.id) + return gostCrypto.asn1.GostSubjectPublicKeyInfo.encode(key); + else + throw new NotSupportedError('Key format not supported'); + }); +}; // + +/** + * The wrapKey method returns a new Promise object that will wrapped key(s). + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-wrapKey}

+ * + * Supported algorithm names: + *
    + *
  • GOST 28147-KW Key Wrapping GOST 28147 modes
  • + *
  • GOST R 34.12-KW Key Wrapping GOST R 34.12 modes
  • + *
  • GOST 28147-MASK Key Mask GOST 28147 modes
  • + *
  • GOST R 34.12-MASK Key Mask GOST R 34.12 modes
  • + *
  • GOST R 34.10-MASK Key Mask GOST R 34.10 modes
  • + *
+ * For additional modes see {@link GostCipher}
+ * + * @memberOf SubtleCrypto + * @method wrapKey + * @instance + * @param {KeyFormat} format Format specifies a serialization format for a key. Now suppored only 'raw' key format. + * @param {Key} key Key object + * @param {Key} wrappingKey Wrapping key object + * @param {AlgorithmIdentifier} wrapAlgorithm Algorithm identifier + * @returns {Promise} Promise that resolves with {@link CryptoOperationData} + */ +SubtleCrypto.prototype.wrapKey = function (format, key, wrappingKey, wrapAlgorithm) // +{ + return new Promise(call).then(function () { + if (checkNative(wrapAlgorithm)) + return rootCrypto.subtle.wrapKey(format, key, wrappingKey, wrapAlgorithm); + + wrapAlgorithm = normalize(wrapAlgorithm, 'wrapKey'); + var keyData = extractKey(null, null, key); + if (wrapAlgorithm.procreator === 'SC' && key.type === 'private') + keyData = swapBytes(keyData); + return execute(wrapAlgorithm, 'wrapKey', + [extractKey('wrapKey', wrapAlgorithm, wrappingKey), keyData]).then(function (data) { + if (format === 'raw') + return data; + else + throw new NotSupportedError('Key format not supported'); + }); + }); +}; // + +/** + * The unwrapKey method returns a new Promise object that will unwrapped key(s). + * WebCrypto API reference {@link http://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-unwrapKey}

+ * + * Supported algorithm names: + *
    + *
  • GOST 28147-KW Key Wrapping GOST 28147 modes
  • + *
  • GOST R 34.12-KW Key Wrapping GOST R 34.12 modes
  • + *
  • GOST 28147-MASK Key Mask GOST 28147 modes
  • + *
  • GOST R 34.12-MASK Key Mask GOST R 34.12 modes
  • + *
  • GOST R 34.10-MASK Key Mask GOST R 34.10 modes
  • + *
+ * For additional modes see {@link GostCipher}
+ * + * @memberOf SubtleCrypto + * @method unwrapKey + * @instance + * @param {KeyFormat} format Format specifies a serialization format for a key. Now suppored only 'raw' key format. + * @param {CryptoOperationData} wrappedKey Wrapped key data + * @param {Key} unwrappingKey Unwrapping key object + * @param {AlgorithmIdentifier} unwrapAlgorithm Algorithm identifier + * @param {AlgorithmIdentifier} unwrappedKeyAlgorithm Key algorithm identifier + * @param {boolean} extractable Whether or not the raw keying material may be exported by the application + * @param {KeyUsages} keyUsages Key usage array: type of operation that may be performed using a key + * @returns {Promise} Promise that resolves with {@link Key} + */ +SubtleCrypto.prototype.unwrapKey = function (format, wrappedKey, unwrappingKey, + unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsages) // +{ + return new Promise(call).then(function () { + if (checkNative(unwrapAlgorithm)) + return rootCrypto.subtle.unwrapKey(format, wrappedKey, unwrappingKey, + unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsages); + + unwrapAlgorithm = normalize(unwrapAlgorithm, 'unwrapKey'); + unwrappedKeyAlgorithm = normalize(unwrappedKeyAlgorithm, 'importKey'); + if (format !== 'raw') + throw new NotSupportedError('Key format not supported'); + + return execute(unwrapAlgorithm, 'unwrapKey', [extractKey('unwrapKey', unwrapAlgorithm, unwrappingKey), wrappedKey]).then(function (data) { + var type; + if (unwrappedKeyAlgorithm && unwrappedKeyAlgorithm.name) { + var name = unwrappedKeyAlgorithm.name.toUpperCase().replace(/[\.\s]/g, ''); + if (name.indexOf('3410') >= 0 && keyUsages.indexOf('sign') >= 0) + type = 'private'; + else if (name.indexOf('3410') >= 0 && keyUsages.indexOf('verify') >= 0) + type = 'public'; + } + if (unwrapAlgorithm.procreator === 'SC' && type === 'private') + data = swapBytes(data); + return convertKey(unwrappedKeyAlgorithm, extractable, keyUsages, data, type); + }); + }); +}; // + +/** + * The subtle attribute provides an instance of the SubtleCrypto + * interface which provides low-level cryptographic primitives and + * algorithms. + * + * @memberOf gostCrypto + * @type SubtleCrypto + */ +gostCrypto.subtle = new SubtleCrypto(); + +/** + * The getRandomValues method generates cryptographically random values. + * + * First try to use Web Crypto random genereator. Next make random + * bytes based on standart Math.random mixed with time and mouse pointer + * + * @memberOf gostCrypto + * @param {(CryptoOperationData)} array Destination buffer for random data + */ +gostCrypto.getRandomValues = function (array) // +{ + // Execute randomizer + GostRandom = GostRandom || root.GostRandom; + var randomSource = GostRandom ? new GostRandom() : rootCrypto; + if (randomSource.getRandomValues) + randomSource.getRandomValues(array); + else + throw new NotSupportedError('Random generator not found'); +}; // +//
+ +export default gostCrypto; diff --git a/src/core/vendor/gost/gostDigest.mjs b/src/core/vendor/gost/gostDigest.mjs new file mode 100644 index 00000000..f8dd5704 --- /dev/null +++ b/src/core/vendor/gost/gostDigest.mjs @@ -0,0 +1,1260 @@ +/** + * GOST R 34.11-94 / GOST R 34.11-12 implementation + * 1.76 + * 2014-2016, Rudolf Nickolaev. All rights reserved. + * + * Exported for CyberChef by mshwed [m@ttshwed.com] + */ + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Converted to JavaScript from source https://www.streebog.net/ + * Copyright (c) 2013, Alexey Degtyarev. + * All rights reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + import GostRandom from './gostRandom.mjs'; + import GostCipher from './gostCipher.mjs'; + import crypto from 'crypto'; + +/* + * GOST R 34.11 + * Common methods + * + */ // + +var root = {}; +var rootCrypto = crypto + +var DataError = Error, + NotSupportedError = Error; + +// Copy len values from s[sOfs] to d[dOfs] +function arraycopy(s, sOfs, d, dOfs, len) { + for (var i = 0; i < len; i++) + d[dOfs + i] = s[sOfs + i]; +} + +// Swap bytes in buffer +function swap(s) { + var src = new Uint8Array(s), + dst = new Uint8Array(src.length); + for (var i = 0, n = src.length; i < n; i++) + dst[n - i - 1] = src[i]; + return dst.buffer; +} + +// Convert BASE64 string to Uint8Array +// for decompression of constants and precalc values +function b64decode(s) { + // s = s.replace(/[^A-Za-z0-9\+\/]/g, ''); + var n = s.length, + k = n * 3 + 1 >> 2, r = new Uint8Array(k); + + for (var m3, m4, u24 = 0, j = 0, i = 0; i < n; i++) { + m4 = i & 3; + var c = s.charCodeAt(i); + + c = c > 64 && c < 91 ? + c - 65 : c > 96 && c < 123 ? + c - 71 : c > 47 && c < 58 ? + c + 4 : c === 43 ? + 62 : c === 47 ? + 63 : 0; + + u24 |= c << 18 - 6 * m4; + if (m4 === 3 || n - i === 1) { + for (m3 = 0; m3 < 3 && j < k; m3++, j++) { + r[j] = u24 >>> (16 >>> m3 & 24) & 255; + } + u24 = 0; + + } + } + return r.buffer; +} + +// Random seed +function getSeed(length) { + GostRandom = GostRandom || root.GostRandom; + var randomSource = GostRandom ? new (GostRandom || root.GostRandom) : rootCrypto; + if (randomSource.getRandomValues) { + var d = new Uint8Array(Math.ceil(length / 8)); + randomSource.getRandomValues(d); + return d; + } else + throw new NotSupportedError('Random generator not found'); +} + +// Check buffer +function buffer(d) { + if (d instanceof ArrayBuffer) + return d; + else if (d && d.buffer && d.buffer instanceof ArrayBuffer) + return d.byteOffset === 0 && d.byteLength === d.buffer.byteLength ? + d.buffer : new Uint8Array(new Uint8Array(d, d.byteOffset, d.byteLength)).buffer; + else + throw new DataError('ArrayBuffer or ArrayBufferView required'); +} // + +/** + * Algorithm name GOST R 34.11 or GOST R 34.11-12

+ * + * http://tools.ietf.org/html/rfc6986 + * + * The digest method returns digest data in according to GOST R 4311-2012.
+ * Size of digest also defines in algorithm name. + *
    + *
  • GOST R 34.11-256-12 - 256 bits digest
  • + *
  • GOST R 34.11-512-12 - 512 bits digest
  • + *
+ * + * @memberOf GostDigest + * @method digest + * @instance + * @param {(ArrayBuffer|TypedArray)} data Data + * @returns {ArrayBuffer} Digest of data + */ +var digest2012 = (function () // +{ + // Constants + var buffer0 = new Int32Array(16); // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + var buffer512 = new Int32Array(16); // [512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + buffer512[0] = 512; + + // Constant C + var C = (function (s) { + var h = new Int32Array(b64decode(s)), + r = new Array(12); + for (var i = 0; i < 12; i++) + r[i] = new Int32Array(h.buffer, i * 64, 16); + return r; + })( + 'B0Wm8lllgN0jTXTMNnR2BRXTYKQIKkKiAWlnkpHgfEv8xIV1jbhOcRbQRS5DdmovH3xlwIEvy+vp2soe2lsIsbebsSFwBHnmVs3L1xui3VXKpwrbwmG1XFiZ1hJrF7WaMQG1Fg9e1WGYKyMKcur+89e1cA9GneNPGi+dqYq1o2+yCroK9ZYemTHbeoZD9LbCCdtiYDc6ycGxnjWQ5A/i03t7KbEUderyix+cUl9e8QY1hD1qKPw5Cscvzius3HT1LtHjhLy+DCLxN+iToepTNL4DUpMzE7fYddYD7YIs16k/NV5orRxynX08XDN+hY5I3eRxXaDhSPnSZhXos98f71f+bHz9WBdg9WPqqX6iVnoWGicjtwD/36P1OiVHF82/vf8PgNc1njVKEIYWHxwVf2MjqWwMQT+amUdHraxr6ktufWRGekBo+jVPkDZyxXG/tsa+wmYf8gq0t5oct6b6z8aO8Jq0mn8YbKRCUfnEZi3AOTB6O8Okb9nTOh2urk+uk9QUOk1WhojzSjyiTEUXNQQFSiiDaUcGNyyCLcWrkgnJk3oZMz5H08mHv+bHxp45VAkkv/6GrFHsxaruFg7H9B7nAr/UDX+k' + + '2ahRWTXCrDYvxKXRK43RaZAGm5LLK4n0msTbTTtEtIke3jaccfi3TkFBbgwCqucDp8mTTUJbH5vbWiODUURhcmAqH8uS3DgOVJwHppqKK3uxzrLbC0QKgIQJDeC3Vdk8JEKJJRs6fTreXxbs2JpMlJsiMRZUWo837ZxFmPvHtHTDtjsV0fqYNvRSdjswbB56SzNprwJn558DYTMbiuH/H9t4iv8c50GJ8/PkskjlKjhSbwWApt6+qxst84HNpMprXdhvwEpZot6Ybkd9Hc2678q5SOrvcR2KeWaEFCGAASBhB6vru2v62JT+WmPNxgIw+4nI79CezXsg1xvxSpK8SJkbstnVF/T6UijhiKqkHeeGzJEYne+AXZufITDUEiD4dx3fvDI8pM16sUkEsIAT0roxFvFn5443'); + + // Precalc Ax + var Ax = (function (s) { + return new Int32Array(b64decode(s)); + })( + '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'); //== + + // Variables + var sigma, N, h; + + // 64bit tools + function get8(x, i) { + return (x[i >> 2] >> ((i & 3) << 3)) & 0xff; + } + + // 512bit tools + function add512(x, y) { + var CF = 0, w0, w1; + for (var i = 0; i < 16; i++) { + w0 = (x[i] & 0xffff) + (y[i] & 0xffff) + (CF || 0); + w1 = (x[i] >>> 16) + (y[i] >>> 16) + (w0 >>> 16); + x[i] = (w0 & 0xffff) | (w1 << 16); + CF = (w1 >>> 16); + } + } + + function get512(d) { + return new Int32Array(d.buffer, d.byteOffset, 16); + } + + + function copy512(r, d) { + for (var i = 0; i < 16; i++) + r[i] = d[i]; + } + + function new512() { + return new Int32Array(16); + } + + // Core private algorithms + function xor512(x, y) { + for (var i = 0; i < 16; i++) + x[i] = x[i] ^ y[i]; + } + + + var r = new512(); + function XLPS(x, y) { + copy512(r, x); + xor512(r, y); + for (var i = 0; i < 8; i++) { + var z0, z1, k = get8(r, i) << 1; + z0 = Ax[k]; + z1 = Ax[k + 1]; + for (var j = 1; j < 8; j++) { + k = (j << 9) + (get8(r, (j << 3) + i) << 1); + z0 = z0 ^ Ax[k]; + z1 = z1 ^ Ax[k + 1]; + } + x[i << 1] = z0; + x[(i << 1) + 1] = z1; + } + } + + var data = new512(), Ki = new512(); + function g(h, N, m) + { + var i; + + copy512(data, h); + XLPS(data, N); + + /* Starting E() */ + copy512(Ki, data); + XLPS(data, m); + + for (i = 0; i < 11; i++) { + XLPS(Ki, C[i]); + XLPS(data, Ki); + } + + XLPS(Ki, C[11]); + xor512(data, Ki); + /* E() done */ + + xor512(h, data); + xor512(h, m); + } + + // Stages + function stage2(d) { + var m = get512(d); + g(h, N, m); + + add512(N, buffer512); + add512(sigma, m); + } + + function stage3(d) { + var n = d.length; + if (n > 63) + return; + + var b0 = new Int32Array(16); + b0[0] = n << 3; + + var b = new Uint8Array(64); + for (var i = 0; i < n; i++) + b[i] = d[i]; + b[n] = 0x01; + + var m = get512(b), m0 = get512(b0); + g(h, N, m); + + add512(N, m0); + add512(sigma, m); + + g(h, buffer0, N); + g(h, buffer0, sigma); + } + + return function (data) { + + // Cleanup + sigma = new512(); + N = new512(); + + // Initial vector + h = new512(); + for (var i = 0; i < 16; i++) + if (this.bitLength === 256) + h[i] = 0x01010101; + + // Make data + var d = new Uint8Array(buffer(data)); + + var n = d.length; + var r = n % 64, q = (n - r) / 64; + + for (var i = 0; i < q; i++) + stage2.call(this, new Uint8Array(d.buffer, i * 64, 64)); + + stage3.call(this, new Uint8Array(d.buffer, q * 64, r)); + + var digest; + if (this.bitLength === 256) { + digest = new Int32Array(8); + for (var i = 0; i < 8; i++) + digest[i] = h[8 + i]; + } else { + digest = new Int32Array(16); + for (var i = 0; i < 16; i++) + digest[i] = h[i]; + } + // Swap hash for SignalCom + if (this.procreator === 'SC' || this.procreator === 'VN') + return swap(digest.buffer); + else + return digest.buffer; + }; +} // +)(); + +/** + * Algorithm name GOST R 34.11-94

+ * + * http://tools.ietf.org/html/rfc5831 + * + * The digest method returns digest data in according to GOST R 34.11-94. + * @memberOf GostDigest + * @method digest + * @instance + * @param {(ArrayBuffer|TypedArray)} data Data + * @returns {ArrayBuffer} Digest of data + */ +var digest94 = (function () // +{ + var C, H, M, Sum; + + // (i + 1 + 4(k - 1)) = 8i + k i = 0-3, k = 1-8 + function P(d) { + var K = new Uint8Array(32); + + for (var k = 0; k < 8; k++) { + K[4 * k] = d[k]; + K[1 + 4 * k] = d[ 8 + k]; + K[2 + 4 * k] = d[16 + k]; + K[3 + 4 * k] = d[24 + k]; + } + + return K; + } + + //A (x) = (x0 ^ x1) || x3 || x2 || x1 + function A(d) + { + var a = new Uint8Array(8); + + for (var j = 0; j < 8; j++) + { + a[j] = (d[j] ^ d[j + 8]); + } + + arraycopy(d, 8, d, 0, 24); + arraycopy(a, 0, d, 24, 8); + + return d; + } + + // (in:) n16||..||n1 ==> (out:) n1^n2^n3^n4^n13^n16||n16||..||n2 + function fw(d) { + var wS = new Uint16Array(d.buffer, 0, 16); + var wS15 = wS[0] ^ wS[1] ^ wS[2] ^ wS[3] ^ wS[12] ^ wS[15]; + arraycopy(wS, 1, wS, 0, 15); + wS[15] = wS15; + } + + //Encrypt function, ECB mode + function encrypt(key, s, sOff, d, dOff) { + var t = new Uint8Array(8); + arraycopy(d, dOff, t, 0, 8); + var r = new Uint8Array(this.cipher.encrypt(key, t)); + arraycopy(r, 0, s, sOff, 8); + } + + // block processing + function process(d, dOff) { + var S = new Uint8Array(32), U = new Uint8Array(32), + V = new Uint8Array(32), W = new Uint8Array(32); + + arraycopy(d, dOff, M, 0, 32); + + //key step 1 + + // H = h3 || h2 || h1 || h0 + // S = s3 || s2 || s1 || s0 + arraycopy(H, 0, U, 0, 32); + arraycopy(M, 0, V, 0, 32); + for (var j = 0; j < 32; j++) + { + W[j] = (U[j] ^ V[j]); + } + // Encrypt GOST 28147-ECB + encrypt.call(this, P(W), S, 0, H, 0); // s0 = EK0 [h0] + + //keys step 2,3,4 + for (var i = 1; i < 4; i++) { + var tmpA = A(U); + for (var j = 0; j < 32; j++) { + U[j] = (tmpA[j] ^ C[i][j]); + } + V = A(A(V)); + for (var j = 0; j < 32; j++) { + W[j] = (U[j] ^ V[j]); + } + // Encrypt GOST 28147-ECB + encrypt.call(this, P(W), S, i * 8, H, i * 8); // si = EKi [hi] + } + + // x(M, H) = y61(H^y(M^y12(S))) + for (var n = 0; n < 12; n++) { + fw(S); + } + for (var n = 0; n < 32; n++) { + S[n] = (S[n] ^ M[n]); + } + + fw(S); + + for (var n = 0; n < 32; n++) { + S[n] = (H[n] ^ S[n]); + } + for (var n = 0; n < 61; n++) { + fw(S); + } + arraycopy(S, 0, H, 0, H.length); + } + + + // 256 bitsblock modul -> (Sum + a mod (2^256)) + function summing(d) + { + var carry = 0; + for (var i = 0; i < Sum.length; i++) + { + var sum = (Sum[i] & 0xff) + (d[i] & 0xff) + carry; + + Sum[i] = sum; + + carry = sum >>> 8; + } + } + + // reset the chaining variables to the IV values. + var C2 = new Uint8Array([ + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF + ]); + + return function (data) { + + // Reset buffers + H = new Uint8Array(32); + M = new Uint8Array(32); + Sum = new Uint8Array(32); + + // Reset IV value + C = new Array(4); + for (var i = 0; i < 4; i++) + C[i] = new Uint8Array(32); + arraycopy(C2, 0, C[2], 0, C2.length); + + // Make data + var d = new Uint8Array(buffer(data)); + + var n = d.length; + var r = n % 32, q = (n - r) / 32; + + // Proccess full blocks + for (var i = 0; i < q; i++) { + var b = new Uint8Array(d.buffer, i * 32, 32); + + summing.call(this, b); // calc sum M + process.call(this, b, 0); + } + + // load d the remadder with padding zero; + if (r > 0) { + var b = new Uint8Array(d.buffer, q * 32), + c = new Uint8Array(32); + arraycopy(b, 0, c, 0, r); + summing.call(this, c); // calc sum M + process.call(this, c, 0); + + } + + // get length into L (byteCount * 8 = bitCount) in little endian. + var L = new Uint8Array(32), n8 = n * 8, k = 0; + while (n8 > 0) { + L[k++] = n8 & 0xff; + n8 = Math.floor(n8 / 256); + } + process.call(this, L, 0); + process.call(this, Sum, 0); + + var h = H.buffer; + + // Swap hash for SignalCom + if (this.procreator === 'SC') + h = swap(h); + + return h; + }; + +} // +)(); + +/** + * Algorithm name SHA-1

+ * + * https://tools.ietf.org/html/rfc3174 + * + * The digest method returns digest data in according to SHA-1.
+ * + * @memberOf GostDigest + * @method digest + * @instance + * @param {(ArrayBuffer|TypedArray)} data Data + * @returns {ArrayBuffer} Digest of data + */ +var digestSHA1 = (function () // +{ + + // Create a buffer for each 80 word block. + var state, block = new Uint32Array(80); + + function common(a, e, w, k, f) { + return (f + e + w + k + ((a << 5) | (a >>> 27))) >>> 0; + } + + function f1(a, b, c, d, e, w) { + return common(a, e, w, 0x5A827999, d ^ (b & (c ^ d))); + } + + function f2(a, b, c, d, e, w) { + return common(a, e, w, 0x6ED9EBA1, b ^ c ^ d); + } + + function f3(a, b, c, d, e, w) { + return common(a, e, w, 0x8F1BBCDC, (b & c) | (d & (b | c))); + } + + function f4(a, b, c, d, e, w) { + return common(a, e, w, 0xCA62C1D6, b ^ c ^ d); + } + + function cycle(state, block) { + var a = state[0], + b = state[1], + c = state[2], + d = state[3], + e = state[4]; + + // Partially unroll loops so we don't have to shift variables. + var fn = f1; + for (var i = 0; i < 80; i += 5) { + if (i === 20) { + fn = f2; + } + else if (i === 40) { + fn = f3; + } + else if (i === 60) { + fn = f4; + } + e = fn(a, b, c, d, e, block[i]); + b = ((b << 30) | (b >>> 2)) >>> 0; + d = fn(e, a, b, c, d, block[i + 1]); + a = ((a << 30) | (a >>> 2)) >>> 0; + c = fn(d, e, a, b, c, block[i + 2]); + e = ((e << 30) | (e >>> 2)) >>> 0; + b = fn(c, d, e, a, b, block[i + 3]); + d = ((d << 30) | (d >>> 2)) >>> 0; + a = fn(b, c, d, e, a, block[i + 4]); + c = ((c << 30) | (c >>> 2)) >>> 0; + } + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + } + + // Swap bytes for 32bits word + function swap32(b) { + return ((b & 0xff) << 24) + | ((b & 0xff00) << 8) + | ((b >> 8) & 0xff00) + | ((b >> 24) & 0xff); + } + + // input is a Uint8Array bitstream of the data + return function (data) { + var d = new Uint8Array(buffer(data)), dlen = d.length; + + // Pad the input string length. + var len = dlen + 9; + if (len % 64) { + len += 64 - (len % 64); + } + + state = new Uint32Array(5); + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + state[4] = 0xc3d2e1f0; + + for (var ofs = 0; ofs < len; ofs += 64) { + + // Copy input to block and write padding as needed + for (var i = 0; i < 64; i++) { + var b = 0, + o = ofs + i; + if (o < dlen) { + b = d[o]; + } + else if (o === dlen) { + b = 0x80; + } + else { + // Write original bit length as a 64bit big-endian integer to the end. + var x = len - o - 1; + if (x >= 0 && x < 4) { + b = (dlen << 3 >>> (x * 8)) & 0xff; + } + } + + // Interpret the input bytes as big-endian per the spec + if (i % 4 === 0) { + block[i >> 2] = b << 24; + } + else { + block[i >> 2] |= b << ((3 - (i % 4)) * 8); + } + } + + // Extend the block + for (var i = 16; i < 80; i++) { + var w = block[i - 3] ^ block[i - 8] ^ block[i - 14] ^ block[i - 16]; + block[i] = (w << 1) | (w >>> 31); + } + + cycle(state, block); + + } + + // Swap the bytes around since they are big endian internally + for (var i = 0; i < 5; i++) + state[i] = swap32(state[i]); + return state.buffer; + }; + +} // +)(); + +/** + * Algorithm name GOST R 34.11-HMAC

+ * + * HMAC with the specified hash function. + * @memberOf GostDigest + * @method sign + * @instance + * @param {ArrayBuffer} key The key for HMAC. + * @param {Hash} data Data + */ +function signHMAC(key, data) // +{ + // GOST R 34.11-94 - B=32b, L=32b + // GOST R 34.11-256 - B=64b, L=32b + // GOST R 34.11-512 - B=64b, L=64b + var b = (this.digest === digest94) ? 32 : 64, + l = this.bitLength / 8, + k = buffer(key), + d = buffer(data), k0; + if (k.byteLength === b) + k0 = new Uint8Array(k); + else { + var k0 = new Uint8Array(b); + if (k.byteLength > b) { + k0.set(new Uint8Array(this.digest(k))); + } else { + k0.set(new Uint8Array(k)); + } + } + var s0 = new Uint8Array(b + d.byteLength), + s1 = new Uint8Array(b + l); + for (var i = 0; i < b; i++) { + s0[i] = k0[i] ^ 0x36; + s1[i] = k0[i] ^ 0x5C; + } + s0.set(new Uint8Array(d), b); + s1.set(new Uint8Array(this.digest(s0)), b); + return this.digest(s1); +} // + +/** + * Algorithm name GOST R 34.11-HMAC

+ * + * Verify HMAC based on GOST R 34.11 hash + * + * @memberOf GostDigest + * @method verify + * @instance + * @param {(ArrayBuffer|TypedArray)} key Key which used for HMAC generation + * @param {(ArrayBuffer|TypedArray)} signature generated HMAC + * @param {(ArrayBuffer|TypedArray)} data Data + * @returns {boolean} HMAC verified = true + */ +function verifyHMAC(key, signature, data) // +{ + var hmac = new Uint8Array(this.sign(key, data)), + test = new Uint8Array(signature); + if (hmac.length !== test.length) + return false; + for (var i = 0, n = hmac.length; i < n; i++) + if (hmac[i] !== test[i]) + return false; + return true; +} // + + +/** + * Algorithm name GOST R 34.11-KDF

+ * + * Simple generate key 256/512 bit random seed for derivation algorithms + * + * @memberOf GostDigest + * @method generateKey + * @instance + * @returns {ArrayBuffer} Generated key + */ +function generateKey() // +{ + return getSeed(this.bitLength).buffer; +} // + +/** + * Algorithm name GOST R 34.11-PFXKDF

+ * + * Derive bits from password (PKCS12 mode) + *
    + *
  • algorithm.salt - random value, salt
  • + *
  • algorithm.iterations - number of iterations
  • + *
+ * @memberOf GostDigest + * @method deriveBits + * @instance + * @param {ArrayBuffer} baseKey - password after UTF-8 decoding + * @param {number} length output bit-length + * @returns {ArrayBuffer} result + */ +function deriveBitsPFXKDF(baseKey, length) // +{ + if (length % 8 > 0) + throw new DataError('Length must multiple of 8'); + var u = this.bitLength / 8, v = (this.digest === digest94) ? 32 : 64, + n = length / 8, r = this.iterations; + // 1. Construct a string, D (the "diversifier"), by concatenating v/8 + // copies of ID. + var ID = this.diversifier, D = new Uint8Array(v); + for (var i = 0; i < v; i++) + D[i] = ID; + // 2. Concatenate copies of the salt together to create a string S of + // length v(ceiling(s/v)) bits (the final copy of the salt may be + // truncated to create S). Note that if the salt is the empty + // string, then so is S. + var S0 = new Uint8Array(buffer(this.salt)), s = S0.length, + slen = v * Math.ceil(s / v), S = new Uint8Array(slen); + for (var i = 0; i < slen; i++) + S[i] = S0[i % s]; + // 3. Concatenate copies of the password together to create a string P + // of length v(ceiling(p/v)) bits (the final copy of the password + // may be truncated to create P). Note that if the password is the + // empty string, then so is P. + var P0 = new Uint8Array(buffer(baseKey)), p = P0.length, + plen = v * Math.ceil(p / v), P = new Uint8Array(plen); + for (var i = 0; i < plen; i++) + P[i] = P0[i % p]; + // 4. Set I=S||P to be the concatenation of S and P. + var I = new Uint8Array(slen + plen); + arraycopy(S, 0, I, 0, slen); + arraycopy(P, 0, I, slen, plen); + // 5. Set c=ceiling(n/u). + var c = Math.ceil(n / u); + // 6. For i=1, 2, ..., c, do the following: + var A = new Uint8Array(c * u); + for (var i = 0; i < c; i++) { + // A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1, + // H(H(H(... H(D||I)))) + var H = new Uint8Array(v + slen + plen); + arraycopy(D, 0, H, 0, v); + arraycopy(I, 0, H, v, slen + plen); + for (var j = 0; j < r; j++) + H = new Uint8Array(this.digest(H)); + arraycopy(H, 0, A, i * u, u); + // B. Concatenate copies of Ai to create a string B of length v + // bits (the final copy of Ai may be truncated to create B). + var B = new Uint8Array(v); + for (var j = 0; j < v; j++) + B[j] = H[j % u]; + // C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit + // blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by + // setting I_j=(I_j+B+1) mod 2^v for each j. + var k = (slen + plen) / v; + for (j = 0; j < k; j++) { + var cf = 1, w; + for (var l = v - 1; l >= 0; --l) { + w = I[v * j + l] + B[l] + cf; + cf = w >>> 8; + I[v * j + l] = w & 0xff; + } + } + } + // 7. Concatenate A_1, A_2, ..., A_c together to form a pseudorandom + // bit string, A. + // 8. Use the first n bits of A as the output of this entire process. + var R = new Uint8Array(n); + arraycopy(A, 0, R, 0, n); + return R.buffer; +} // + +/** + * Algorithm name GOST R 34.11-KDF

+ * + * Derive bits for KEK deversification in 34.10-2012 algorithm + * KDF(KEK, UKM, label) = HMAC256 (KEK, 0x01|label|0x00|UKM|0x01|0x00) + * Default label = 0x26|0xBD|0xB8|0x78 + * + * @memberOf GostDigest + * @method deriveBits + * @instance + * @param {(ArrayBuffer|TypedArray)} baseKey base key for deriviation + * @param {number} length output bit-length + * @returns {ArrayBuffer} result + */ +function deriveBitsKDF(baseKey, length) // +{ + if (length % 8 > 0) + throw new DataError('Length must be multiple of 8'); + var rlen = length / 8, label, context = new Uint8Array(buffer(this.context)), + blen = this.bitLength / 8, n = Math.ceil(rlen / blen); + if (this.label) + label = new Uint8Array(buffer(this.label)); + else + label = new Uint8Array([0x26, 0xBD, 0xB8, 0x78]); + var result = new Uint8Array(rlen); + for (var i = 0; i < n; i++) { + var data = new Uint8Array(label.length + context.length + 4); + data[0] = i + 1; + data.set(label, 1); + data[label.length + 1] = 0x00; + data.set(context, label.length + 2); + data[data.length - 2] = length >>> 8; + data[data.length - 1] = length & 0xff; + result.set(new Uint8Array(signHMAC.call(this, baseKey, data), 0, + i < n - 1 ? blen : rlen - i * blen), i * blen); + } + return result.buffer; +} // + +/** + * Algorithm name GOST R 34.11-PBKDF1

+ * + * Derive bits from password + *
    + *
  • algorithm.salt - random value, salt
  • + *
  • algorithm.iterations - number of iterations
  • + *
+ * @memberOf GostDigest + * @method deriveBits + * @instance + * @param {ArrayBuffer} baseKey - password after UTF-8 decoding + * @param {number} length output bit-length + * @returns {ArrayBuffer} result + */ +function deriveBitsPBKDF1(baseKey, length) // +{ + if (length < this.bitLength / 2 || length % 8 > 0) + throw new DataError('Length must be more than ' + this.bitLength / 2 + ' bits and multiple of 8'); + var hLen = this.bitLength / 8, dkLen = length / 8, + c = this.iterations, + P = new Uint8Array(buffer(baseKey)), + S = new Uint8Array(buffer(this.salt)), + slen = S.length, plen = P.length, + T = new Uint8Array(plen + slen), + DK = new Uint8Array(dkLen); + if (dkLen > hLen) + throw new DataError('Invalid parameters: Length value'); + arraycopy(P, 0, T, 0, plen); + arraycopy(S, 0, T, plen, slen); + for (var i = 0; i < c; i++) + T = new Uint8Array(this.digest(T)); + arraycopy(T, 0, DK, 0, dkLen); + return DK.buffer; +} // + +/** + * Algorithm name GOST R 34.11-PBKDF2

+ * + * Derive bits from password + *
    + *
  • algorithm.salt - random value, salt
  • + *
  • algorithm.iterations - number of iterations
  • + *
+ * @memberOf GostDigest + * @method deriveBits + * @instance + * @param {ArrayBuffer} baseKey - password after UTF-8 decoding + * @param {number} length output bit-length + * @returns {ArrayBuffer} result + */ +function deriveBitsPBKDF2(baseKey, length) // +{ + var diversifier = this.diversifier || 1; // For PKCS12 MAC required 3*length + length = length * diversifier; + if (length < this.bitLength / 2 || length % 8 > 0) + throw new DataError('Length must be more than ' + this.bitLength / 2 + ' bits and multiple of 8'); + var hLen = this.bitLength / 8, dkLen = length / 8, + c = this.iterations, + P = new Uint8Array(buffer(baseKey)), + S = new Uint8Array(buffer(this.salt)); + var slen = S.byteLength, + data = new Uint8Array(slen + 4); + arraycopy(S, 0, data, 0, slen); + + if (dkLen > (0xffffffff - 1) * 32) + throw new DataError('Invalid parameters: Length value'); + var n = Math.ceil(dkLen / hLen), + DK = new Uint8Array(dkLen); + for (var i = 1; i <= n; i++) { + data[slen] = i >>> 24 & 0xff; + data[slen + 1] = i >>> 16 & 0xff; + data[slen + 2] = i >>> 8 & 0xff; + data[slen + 3] = i & 0xff; + + var U = new Uint8Array(signHMAC.call(this, P, data)), Z = U; + for (var j = 1; j < c; j++) { + U = new Uint8Array(signHMAC.call(this, P, U)); + for (var k = 0; k < hLen; k++) + Z[k] = U[k] ^ Z[k]; + } + var ofs = (i - 1) * hLen; + arraycopy(Z, 0, DK, ofs, Math.min(hLen, dkLen - ofs)); + } + if (diversifier > 1) { + var rLen = dkLen / diversifier, R = new Uint8Array(rLen); + arraycopy(DK, dkLen - rLen, R, 0, rLen); + return R.buffer; + } else + return DK.buffer; +} // + +/** + * Algorithm name GOST R 34.11-CPKDF

+ * + * Derive bits from password. CryptoPro algorithm + *
    + *
  • algorithm.salt - random value, salt
  • + *
  • algorithm.iterations - number of iterations
  • + *
+ * @memberOf GostDigest + * @method deriveBits + * @instance + * @param {ArrayBuffer} baseKey - password after UTF-8 decoding + * @param {number} length output bit-length + * @returns {ArrayBuffer} result + */ +function deriveBitsCP(baseKey, length) { + if (length > this.bitLength || length % 8 > 0) + throw new DataError('Length can\'t be more than ' + this.bitLength + ' bits and multiple of 8'); + // GOST R 34.11-94 - B=32b, L=32b + // GOST R 34.11-256 - B=64b, L=32b + // GOST R 34.11-512 - B=64b, L=64b + var b = (this.digest === digest94) ? 32 : 64, + l = this.bitLength / 8, + p = baseKey && baseKey.byteLength > 0 ? new Uint8Array(buffer(baseKey)) : false, + plen = p ? p.length : 0, + iterations = this.iterations, + salt = new Uint8Array(buffer(this.salt)), + slen = salt.length, + d = new Uint8Array(slen + plen); + arraycopy(salt, 0, d, 0, slen); + if (p) + arraycopy(p, 0, d, slen, plen); + + var h = new Uint8Array(this.digest(d)), + k = new Uint8Array(b), + s0 = new Uint8Array(b), + s1 = new Uint8Array(b); + var c = 'DENEFH028.760246785.IUEFHWUIO.EF'; + for (var i = 0; i < c.length; i++) + k[i] = c.charCodeAt(i); + + d = new Uint8Array(2 * (b + l)); + for (var j = 0; j < iterations; j++) { + for (var i = 0; i < b; i++) { + s0[i] = k[i] ^ 0x36; + s1[i] = k[i] ^ 0x5C; + k[i] = 0; + } + arraycopy(s0, 0, d, 0, b); + arraycopy(h, 0, d, b, l); + arraycopy(s1, 0, d, b + l, b); + arraycopy(h, 0, d, b + l + b, l); + arraycopy(new Uint8Array(this.digest(d)), 0, k, 0, l); + } + for (var i = 0; i < l; i++) { + s0[i] = k[i] ^ 0x36; + s1[i] = k[i] ^ 0x5C; + k[i] = 0; + } + d = new Uint8Array(2 * l + slen + plen); + arraycopy(s0, 0, d, 0, l); + arraycopy(salt, 0, d, l, slen); + arraycopy(s1, 0, d, l + slen, l); + if (p) + arraycopy(p, 0, d, l + slen + l, plen); + h = this.digest(this.digest(d)); + if (length === this.bitLength) + return h; + else { + var rlen = length / 8, r = new Uint8Array(rlen); + arraycopy(h, 0, r, 0, rlen); + return r.buffer; + } +} + +/** + * Algorithm name GOST R 34.11-KDF or GOST R 34.11-PBKDF2 or other

+ * + * Derive key from derive bits subset + * + * @memberOf GostDigest + * @method deriveKey + * @instance + * @param {ArrayBuffer} baseKey + * @returns {ArrayBuffer} + */ +function deriveKey(baseKey) // +{ + return this.deriveBits(baseKey, this.keySize * 8); +} // + +/** + * GOST R 34.11 Algorithm

+ * + * References: {@link http://tools.ietf.org/html/rfc6986} and {@link http://tools.ietf.org/html/rfc5831}

+ * + * Normalized algorithm identifier common parameters: + * + *
    + *
  • name Algorithm name 'GOST R 34.11'
  • + *
  • version Algorithm version + *
      + *
    • 1994 old-style 256 bits digest based on GOST 28147-89
    • + *
    • 2012 256 ro 512 bits digest algorithm "Streebog" GOST R 34.11-2012 (default)
    • + *
    + *
  • + *
  • length Digest length + *
      + *
    • 256 256 bits digest
    • + *
    • 512 512 bits digest, valid only for algorithm "Streebog"
    • + *
    + *
  • + *
  • mode Algorithm mode + *
      + *
    • HASH simple digest mode (default)
    • + *
    • HMAC HMAC algorithm based on GOST R 34.11
    • + *
    • KDF Derive bits for KEK deversification
    • + *
    • PBKDF2 Password based key dirivation algorithms PBKDF2 (based on HMAC)
    • + *
    • PFXKDF Password based PFX key dirivation algorithms
    • + *
    • CPKDF CpyptoPro Password based key dirivation algorithms
    • + *
    + *
  • + *
  • sBox Paramset sBox for GOST 28147-89. Used only if version = 1994
  • + *
+ * + * Supported algorithms, modes and parameters: + * + *
    + *
  • Digest HASH mode (default)
  • + *
  • Sign/Verify HMAC modes parameters depends on version and length + *
      + *
    • version: 1994 HMAC parameters (B = 32, L = 32)
    • + *
    • version: 2012, length: 256 HMAC parameters (B = 64, L = 32)
    • + *
    • version: 2012, length: 512 HMAC parameters (B = 64, L = 64)
    • + *
    + *
  • + *
  • DeriveBits/DeriveKey KDF mode + *
      + *
    • context {@link CryptoOperationData} Context of the key derivation
    • + *
    • label {@link CryptoOperationData} Label that identifies the purpose for the derived keying material
    • + *
    + *
  • + *
  • DeriveBits/DeriveKey PBKDF2 mode + *
      + *
    • salt {@link CryptoOperationData} Random salt as input for HMAC algorithm
    • + *
    • iterations Iteration count. GOST recomended value 1000 (default) or 2000
    • + *
    • diversifier Deversifier, ID=1 - key material for performing encryption or decryption, ID=3 - integrity key for MACing
    • + *
    + *
  • + *
  • DeriveBits/DeriveKey PFXKDF mode + *
      + *
    • salt {@link CryptoOperationData} Random salt as input for HMAC algorithm
    • + *
    • iterations Iteration count. GOST recomended value 1000 (default) or 2000
    • + *
    • diversifier Deversifier, ID=1 - key material for performing encryption or decryption, + * ID=2 - IV (Initial Value) for encryption or decryption, ID=3 - integrity key for MACing
    • + *
    + *
  • + *
  • DeriveBits/DeriveKey CPKDF mode + *
      + *
    • salt {@link CryptoOperationData} Random salt as input for HMAC algorithm
    • + *
    • iterations Iteration count. GOST recomended value 1000 (default) or 2000
    • + *
    + *
  • + *
+ * + * @class GostDigest + * @param {AlgorithmIdentifier} algorithm WebCryptoAPI algorithm identifier + */ +function GostDigest(algorithm) // +{ + + algorithm = algorithm || {}; + + this.name = (algorithm.name || 'GOST R 34.10') + '-' + ((algorithm.version || 2012) % 100) + + ((algorithm.version || 2012) > 1 ? '-' + (algorithm.length || 256) : '') + + (((algorithm.mode || 'HASH') !== 'HASH') ? '-' + algorithm.mode : '') + + (algorithm.procreator ? '/' + algorithm.procreator : '') + + (typeof algorithm.sBox === 'string' ? '/' + algorithm.sBox : ''); + + // Algorithm procreator + this.procreator = algorithm.procreator; + + // Bit length + this.bitLength = algorithm.length || 256; + + switch (algorithm.version || 2012) { + case 1: // SHA-1 + this.digest = digestSHA1; + this.bitLength = 160; + break; + case 1994: + this.digest = digest94; + // Define chiper algorithm + this.sBox = (algorithm.sBox || (algorithm.procreator === 'SC' ? 'D-SC' : 'D-A')).toUpperCase(); + + //if (!GostCipher) + // GostCipher = root.GostCipher; + if (!GostCipher) + throw new NotSupportedError('Object GostCipher not found'); + + this.cipher = new GostCipher({ + name: 'GOST 28147', + block: 'ECB', + sBox: this.sBox, + procreator: this.procreator + }); + + break; + case 2012: + this.digest = digest2012; + break; + default: + throw new NotSupportedError('Algorithm version ' + algorithm.version + ' not supported'); + } + + // Key size + this.keySize = algorithm.keySize || (algorithm.version <= 2 ? this.bitLength / 8 : 32); + + switch (algorithm.mode || 'HASH') { + case 'HASH': + break; + case 'HMAC': + this.sign = signHMAC; + this.verify = verifyHMAC; + this.generateKey = generateKey; + break; + case 'KDF': + this.deriveKey = deriveKey; + this.deriveBits = deriveBitsKDF; + this.label = algorithm.label; + this.context = algorithm.context; + break; + case 'PBKDF2': + this.deriveKey = deriveKey; + this.deriveBits = deriveBitsPBKDF2; + this.generateKey = generateKey; + this.salt = algorithm.salt; + this.iterations = algorithm.iterations || 2000; + this.diversifier = algorithm.diversifier || 1; + break; + case 'PFXKDF': + this.deriveKey = deriveKey; + this.deriveBits = deriveBitsPFXKDF; + this.generateKey = generateKey; + this.salt = algorithm.salt; + this.iterations = algorithm.iterations || 2000; + this.diversifier = algorithm.diversifier || 1; + break; + case 'CPKDF': + this.deriveKey = deriveKey; + this.deriveBits = deriveBitsCP; + this.generateKey = generateKey; + this.salt = algorithm.salt; + this.iterations = algorithm.iterations || 2000; + break; + default: + throw new NotSupportedError('Algorithm mode ' + algorithm.mode + ' not supported'); + } +} // + +export default GostDigest; \ No newline at end of file diff --git a/src/core/vendor/gost/gostEngine.mjs b/src/core/vendor/gost/gostEngine.mjs new file mode 100755 index 00000000..fb68a791 --- /dev/null +++ b/src/core/vendor/gost/gostEngine.mjs @@ -0,0 +1,451 @@ +/** + * @file GOST 34.10-2012 signature function with 1024/512 bits digest + * @version 1.76 + * @copyright 2014-2016, Rudolf Nickolaev. All rights reserved. + */ + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +import GostRandom from './gostRandom.mjs'; +import GostCipher from './gostCipher.mjs'; +import GostDigest from './gostDigest.mjs'; +import GostSign from './gostSign.mjs'; + +/* + * Engine definition base on normalized algorithm identifier + * + */ // + +var root = {}; + +// Define engine +function defineEngine(method, algorithm) { + if (!algorithm) + throw new (root.SyntaxError || Error)('Algorithm not defined'); + + if (!algorithm.name) + throw new (root.SyntaxError || Error)('Algorithm name not defined'); + + var name = algorithm.name, mode = algorithm.mode; + if ((name === 'GOST 28147' || name === 'GOST R 34.12' || name === 'RC2') && (method === 'generateKey' || + (mode === 'MAC' && (method === 'sign' || method === 'verify')) || + ((mode === 'KW' || mode === 'MASK') && (method === 'wrapKey' || method === 'unwrapKey')) || + ((!mode || mode === 'ES') && (method === 'encrypt' || method === 'decrypt')))) { + return 'GostCipher'; + + } else if ((name === 'GOST R 34.11' || name === 'SHA') && (method === 'digest' || + (mode === 'HMAC' && (method === 'sign' || method === 'verify' || method === 'generateKey')) || + ((mode === 'KDF' || mode === 'PBKDF2' || mode === 'PFXKDF' || mode === 'CPKDF') && + (method === 'deriveKey' || method === 'deriveBits' || method === 'generateKey')))) { + return 'GostDigest'; + + } else if (name === 'GOST R 34.10' && (method === 'generateKey' || + ((!mode || mode === 'SIGN') && (method === 'sign' || method === 'verify')) || + (mode === 'MASK' && (method === 'wrapKey' || method === 'unwrapKey')) || + (mode === 'DH' && (method === 'deriveKey' || method === 'deriveBits')))) { + return 'GostSign'; + } else + throw new (root.NotSupportedError || Error)('Algorithm ' + name + '-' + mode + ' is not valid for ' + method); +} // + +/** + * Object implements dedicated Web Workers and provide a simple way to create + * and run GOST cryptographic algorithms in background thread. + * + * Object provide interface to GOST low-level cryptogric classes: + *
    + *
  • GostCipher - implementation of GOST 28147, GOST R 34.12, GOST R 34.13 Encryption algorithms. Reference {@link http://tools.ietf.org/html/rfc5830}
  • + *
  • GostDigest - implementation of GOST R 34.11 Hash Function algorithms. References {@link http://tools.ietf.org/html/rfc5831} and {@link http://tools.ietf.org/html/rfc6986}
  • + *
  • GostSign - implementation of GOST R 34.10 Digital Signature algorithms. References {@link http://tools.ietf.org/html/rfc5832} and {@link http://tools.ietf.org/html/rfc7091}
  • + *
+ * @namespace gostEngine + */ +var gostEngine = { + /** + * gostEngine.execute(algorithm, method, args) Entry point to execution + * all low-level GOST cryptographic methods + * + *
    + *
  • Determine the appropriate engine for a given execution method
  • + *
  • Create cipher object for determineted engine
  • + *
  • Execute method of cipher with given args
  • + *
+ * + * @memberOf gostEngine + * @param {AlgorithmIndentifier} algorithm Algorithm identifier + * @param {string} method Crypto method for execution + * @param {Array} args Method arguments (keys, data, additional parameters) + * @returns {(CryptoOperationData|Key|KeyPair|boolean)} Result of method execution + */ + execute: function (algorithm, method, args) // + { + // Define engine for GOST algorithms + var engine = defineEngine(method, algorithm); + // Create cipher + var cipher = this['get' + engine](algorithm); + // Execute method + return cipher[method].apply(cipher, args); + }, // + /** + * gostEngine.getGostCipher(algorithm) returns GOST 28147 / GOST R 34.12 cipher instance

+ * + * GOST 28147-89 / GOST R 34.12-15 Encryption Algorithm

+ * When keys and initialization vectors are converted to/from byte arrays, + * little-endian byte order is assumed.

+ * + * Normalized algorithm identifier common parameters: + * + *
    + *
  • name Algorithm name 'GOST 28147' or 'GOST R 34.12'
  • + *
  • version Algorithm version, number + *
      + *
    • 1989 Current version of standard
    • + *
    • 2015 New draft version of standard
    • + *
    + *
  • + *
  • length Block length + *
      + *
    • 64 64 bits length (default)
    • + *
    • 128 128 bits length (only for version 2015)
    • + *
    + *
  • + *
  • mode Algorithm mode, string + *
      + *
    • ES Encryption mode (default)
    • + *
    • MAC "imitovstavka" (MAC) mode
    • + *
    • KW Key wrapping mode
    • + *
    • MASK Key mask mode
    • + *
    + *
  • + *
  • sBox Paramset sBox for GOST 28147-89, string. Used only if version = 1989
  • + *
+ * + * Supported algorithms, modes and parameters: + * + *
    + *
  • Encript/Decrypt mode (ES) + *
      + *
    • block Block mode, string. Default ECB
    • + *
    • keyMeshing Key meshing mode, string. Default NO
    • + *
    • padding Padding mode, string. Default NO for CFB and CTR modes, or ZERO for others
    • + *
    • iv {@link CryptoOperationData} Initial vector with length of block. Default - zero block
    • + *
    + *
  • + *
  • Sign/Verify mode (MAC) + *
      + *
    • macLength Length of mac in bits (default - 32 bits)
    • + *
    • iv {@link CryptoOperationData} Initial vector with length of block. Default - zero block
    • + *
    + *
  • + *
  • Wrap/Unwrap key mode (KW) + *
      + *
    • keyWrapping Mode of keywrapping, string. Default NO - standard GOST key wrapping
    • + *
    • ukm {@link CryptoOperationData} User key material. Default - random generated value
    • + *
    + *
  • + *
  • Wrap/Unwrap key mode (MASK)
  • + *
+ * + * Supported paramters values: + * + *
    + *
  • Block modes (parameter 'block') + *
      + *
    • ECB "prostaya zamena" (ECB) mode (default)
    • + *
    • CFB "gammirovanie s obratnoj svyaziyu" (64-bit CFB) mode
    • + *
    • CTR "gammirovanie" (counter) mode
    • + *
    • CBC Cipher-Block-Chaining (CBC) mode
    • + *
    + *
  • + *
  • Key meshing modes (parameter 'keyMeshing') + *
      + *
    • NO No key wrapping (default)
    • + *
    • CP CryptoPor Key key meshing
    • + *
    + *
  • + *
  • Padding modes (parameter 'padding') + *
      + *
    • NO No padding only for CFB and CTR modes
    • + *
    • PKCS5 PKCS#5 padding mode
    • + *
    • ZERO Zero bits padding mode
    • + *
    • RANDOM Random bits padding mode
    • + *
    + *
  • + *
  • Wrapping key modes (parameter 'keyWrapping') + *
      + *
    • NO Ref. rfc4357 6.1 GOST 28147-89 Key wrapping
    • + *
    • CP CryptoPro Key wrapping mode
    • + *
    • SC SignalCom Key wrapping mode
    • + *
    + *
  • + *
+ * + * @memberOf gostEngine + * @param {AlgorithmIndentifier} algorithm Algorithm identifier + * @returns {GostCipher} Instance of GostCipher + */ + getGostCipher: function (algorithm) // + { + return new (GostCipher || (GostCipher = root.GostCipher))(algorithm); + }, // + /** + * gostEngine.getGostDigest(algorithm) returns GOST R 34.11 cipher instance

+ * + * Normalized algorithm identifier common parameters: + * + *
    + *
  • name Algorithm name 'GOST R 34.11'
  • + *
  • version Algorithm version + *
      + *
    • 1994 old-style 256 bits digest based on GOST 28147-89
    • + *
    • 2012 256 ro 512 bits digest algorithm "Streebog" GOST R 34.11-2012 (default)
    • + *
    + *
  • + *
  • length Digest length + *
      + *
    • 256 256 bits digest
    • + *
    • 512 512 bits digest, valid only for algorithm "Streebog"
    • + *
    + *
  • + *
  • mode Algorithm mode + *
      + *
    • HASH simple digest mode (default)
    • + *
    • HMAC HMAC algorithm based on GOST R 34.11
    • + *
    • KDF Derive bits for KEK deversification
    • + *
    • PBKDF2 Password based key dirivation algorithms PBKDF2 (based on HMAC)
    • + *
    • PFXKDF PFX key dirivation algorithms PFXKDF
    • + *
    • CPKDF CryptoPro Password based key dirivation algorithms
    • + *
    + *
  • + *
  • sBox Paramset sBox for GOST 28147-89. Used only if version = 1994
  • + *
+ * + * Supported algorithms, modes and parameters: + * + *
    + *
  • Digest HASH mode (default)
  • + *
  • Sign/Verify HMAC modes parameters depends on version and length + *
      + *
    • version: 1994 HMAC parameters (B = 32, L = 32)
    • + *
    • version: 2012, length: 256 HMAC parameters (B = 64, L = 32)
    • + *
    • version: 2012, length: 512 HMAC parameters (B = 64, L = 64)
    • + *
    + *
  • + *
  • DeriveBits/DeriveKey KDF mode + *
      + *
    • context {@link CryptoOperationData} Context of the key derivation
    • + *
    • label {@link CryptoOperationData} Label that identifies the purpose for the derived keying material
    • + *
    + *
  • + *
  • DeriveBits/DeriveKey PBKDF2 mode + *
      + *
    • salt {@link CryptoOperationData} Random salt as input for HMAC algorithm
    • + *
    • iterations Iteration count. GOST recomended value 1000 (default) or 2000
    • + *
    + *
  • + *
  • DeriveBits/DeriveKey PFXKDF mode + *
      + *
    • salt {@link CryptoOperationData} Random salt as input for HMAC algorithm
    • + *
    • iterations Iteration count. GOST recomended value 1000 (default) or 2000
    • + *
    • diversifier Deversifier, ID=1 - key material for performing encryption or decryption, + * ID=2 - IV (Initial Value) for encryption or decryption, ID=3 - integrity key for MACing
    • + *
    + *
  • + *
  • DeriveBits/DeriveKey CPKDF mode + *
      + *
    • salt {@link CryptoOperationData} Random salt as input for HMAC algorithm
    • + *
    • iterations Iteration count. GOST recomended value 1000 (default) or 2000
    • + *
    + *
  • + *
+ * + * @memberOf gostEngine + * @param {AlgorithmIndentifier} algorithm Algorithm identifier + * @returns {GostDigest} Instance of GostDigest + */ + getGostDigest: function (algorithm) // + { + return new (GostDigest || (GostDigest = root.GostDigest))(algorithm); + }, // + /** + * gostEngine.getGostSign(algorithm) returns GOST R 34.10 cipher instance

+ * + * Normalized algorithm identifier common parameters: + * + *
    + *
  • name Algorithm name 'GOST R 34.10'
  • + *
  • version Algorithm version + *
      + *
    • 1994 - Old-style GOST R 34.10-94 ExpMod algorithm with GOST R 34.11-94 hash
    • + *
    • 2001 - GOST R 34.10-2001 Eliptic curve algorithm with old GOST R 34.11-94 hash
    • + *
    • 2012 - GOST R 34.10-2012 Eliptic curve algorithm with GOST R 34.11-12 hash, default mode
    • + *
    + *
  • + *
  • length Length of hash and signature. Key length == hash length for EC algorithms and 2 * hash length for ExpMod algorithm + *
      + *
    • GOST R 34.10-256 - 256 bits digest, default mode
    • + *
    • GOST R 34.10-512 - 512 bits digest only for GOST R 34.11-2012 hash
    • + *
    + *
  • + *
  • mode Algorithm mode + *
      + *
    • SIGN Digital signature mode (default)
    • + *
    • DH Diffie-Hellman key generation and key agreement mode
    • + *
    • MASK Key mask mode
    • + *
    + *
  • + *
  • sBox Paramset sBox for GOST 34.11-94. Used only if version = 1994 or 2001
  • + *
+ * + * Supported algorithms, modes and parameters: + * + *
    + *
  • Sign/Verify mode (SIGN)
  • + *
  • Wrap/Unwrap mode (MASK)
  • + *
  • DeriveKey/DeriveBits mode (DH) + *
      + *
    • {@link CryptoOperationData} ukm User key material. Default - random generated value
    • + *
    • {@link CryptoOperationData} public The peer's EC public key data
    • + *
    + *
  • + *
  • GenerateKey mode (SIGN and DH and MASK) version = 1994 + *
      + *
    • namedParam Paramset for key generation algorithm. If specified no additianal parameters required
    • + *
    + * Additional parameters, if namedParam not specified + *
      + *
    • modulusLength Bit length of p (512 or 1024 bits). Default = 1024
    • + *
    • p {@link CryptoOperationData} Modulus, prime number, 2^(t-1) + *
    • q {@link CryptoOperationData} Order of cyclic group, prime number, 2^254 + *
    • a {@link CryptoOperationData} Generator, integer, 1 + *
    + *
  • + *
  • GenerateKey mode (SIGN and DH and MASK) version = 2001 or 2012 + *
      + *
    • namedCurve Paramset for key generation algorithm. If specified no additianal parameters required
    • + *
    + * Additional EC parameters, if namedCurve not specified + *
      + *
    • p {@link CryptoOperationData} Prime number - elliptic curve modulus
    • + *
    • a {@link CryptoOperationData} Coefficients a of the elliptic curve E
    • + *
    • b {@link CryptoOperationData} Coefficients b of the elliptic curve E
    • + *
    • q {@link CryptoOperationData} Prime number - order of cyclic group
    • + *
    • x {@link CryptoOperationData} Base point p x-coordinate
    • + *
    • y {@link CryptoOperationData} Base point p y-coordinate
    • + *
    + *
  • + *
+ * + * @memberOf gostEngine + * @param {AlgorithmIndentifier} algorithm Algorithm identifier + * @returns {GostSign} Instance of GostSign + */ + getGostSign: function (algorithm) // + { + return new (GostSign || (GostSign = root.GostSign))(algorithm); + } // +}; + +/* + * Worker method execution + * + */ // + +// Worker for gostCripto method execution +if (root.importScripts) { + + /** + * Method called when {@link SubtleCrypto} calls its own postMessage() + * method with data parameter: algorithm, method and arg.
+ * Call method execute and postMessage() results to onmessage event handler + * in the main process.
+ * If error occured onerror event handler executed in main process. + * + * @memberOf gostEngine + * @name onmessage + * @param {MessageEvent} event Message event with data {algorithm, method, args} + */ + root.onmessage = function (event) { + try { + postMessage({ + id: event.data.id, + result: gostEngine.execute(event.data.algorithm, + event.data.method, event.data.args)}); + } catch (e) { + postMessage({ + id: event.data.id, + error: e.message + }); + } + }; +} else { + + // Load dependens + var baseUrl = '', nameSuffix = ''; + // Try to define from DOM model + if (typeof document !== 'undefined') { + (function () { + var regs = /^(.*)gostCrypto(.*)\.js$/i; + var list = document.querySelectorAll('script'); + for (var i = 0, n = list.length; i < n; i++) { + var value = list[i].getAttribute('src'); + var test = regs.exec(value); + if (test) { + baseUrl = test[1]; + nameSuffix = test[2]; + } + } + })(); + } + + // Local importScripts procedure for include dependens + var importScripts = function () { + for (var i = 0, n = arguments.length; i < n; i++) { + var name = arguments[i].split('.'), + src = baseUrl + name[0] + nameSuffix + '.' + name[1]; + var el = document.querySelector('script[src="' + src + '"]'); + if (!el) { + el = document.createElement('script'); + el.setAttribute('src', src); + document.head.appendChild(el); + } + } + }; + + // Import engines + if (!GostRandom) + importScripts('gostRandom.js'); + if (!GostCipher) + importScripts('gostCipher.js'); + if (!GostDigest) + importScripts('gostDigest.js'); + if (!GostSign) + importScripts('gostSign.js'); +} //
+ +export default gostEngine; + diff --git a/src/core/vendor/gost/gostRandom.mjs b/src/core/vendor/gost/gostRandom.mjs new file mode 100644 index 00000000..03ce8937 --- /dev/null +++ b/src/core/vendor/gost/gostRandom.mjs @@ -0,0 +1,128 @@ +/** + * Implementation Web Crypto random generatore for GOST algorithms + * 1.76 + * 2014-2016, Rudolf Nickolaev. All rights reserved. + * + * Exported for CyberChef by mshwed [m@ttshwed.com] + */ + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +import crypto from 'crypto'; + + +/** + * The gostCrypto provide general purpose cryptographic functionality for + * GOST standards including a cryptographically strong pseudo-random number + * generator seeded with truly random values. + * + * @Class GostRandom + * + */ // + +var root = {}; +var rootCrypto = crypto; + +var TypeMismatchError = Error; +var QuotaExceededError = Error; + +// Initialize mouse and time counters for random generator +var randomRing = { + seed: new Uint8Array(1024), + getIndex: 0, + setIndex: 0, + set: function (x) { + if (this.setIndex >= 1024) + this.setIndex = 0; + this.seed[this.setIndex++] = x; + }, + get: function () { + if (this.getIndex >= 1024) + this.getIndex = 0; + return this.seed[this.getIndex++]; + } +}; + +if (typeof document !== 'undefined') { + try { + // Mouse move event to fill random array + document.addEventListener('mousemove', function (e) { + randomRing.set((new Date().getTime() & 255) ^ + ((e.clientX || e.pageX) & 255) ^ + ((e.clientY || e.pageY) & 255)); + }, false); + } catch (e) { + } + + try { + // Keypress event to fill random array + document.addEventListener('keydown', function (e) { + randomRing.set((new Date().getTime() & 255) ^ + (e.keyCode & 255)); + }, false); + } catch (e) { + } +} // + +function GostRandom() { +} + +/** + * The getRandomValues method generates cryptographically random values.

+ * + * Random generator based on JavaScript Web Crypto random genereator + * (if it is possible) or Math.random mixed with time and parameters of + * mouse and keyboard events + * + * @memberOf GostRandom + * @param {(ArrayBuffer|ArrayBufferView)} array Destination buffer for random data + */ +GostRandom.prototype.getRandomValues = function (array) // +{ + + if (!array.byteLength) + throw new TypeMismatchError('Array is not of an integer type (Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, or Uint32Array)'); + + if (array.byteLength > 65536) + throw new QuotaExceededError('Byte length of array can\'t be greate then 65536'); + + var u8 = new Uint8Array(array.buffer, array.byteOffset, array.byteLength); + if (rootCrypto && rootCrypto.getRandomValues) { + // Native window cryptographic interface + rootCrypto.getRandomValues(u8); + } else { + // Standard Javascript method + for (var i = 0, n = u8.length; i < n; i++) + u8[i] = Math.floor(256 * Math.random()) & 255; + } + + // Mix bio randomizator + for (var i = 0, n = u8.length; i < n; i++) + u8[i] = u8[i] ^ randomRing.get(); + return array; +}; // + +export default GostRandom; diff --git a/src/core/vendor/gost/gostSign.mjs b/src/core/vendor/gost/gostSign.mjs new file mode 100755 index 00000000..20fa141b --- /dev/null +++ b/src/core/vendor/gost/gostSign.mjs @@ -0,0 +1,2023 @@ +/** + * @file GOST 34.10-2012 signature function with 1024/512 bits digest + * @version 1.76 + * @copyright 2014-2016, Rudolf Nickolaev. All rights reserved. + */ + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Used library JSBN http://www-cs-students.stanford.edu/~tjw/jsbn/ + * Copyright (c) 2003-2005 Tom Wu (tjw@cs.Stanford.EDU) + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + import GostRandom from './gostRandom.mjs'; + import GostDigest from './gostDigest.mjs'; + + import crypto from 'crypto'; + + /* + * Predefined curves and params collection + * + * http://tools.ietf.org/html/rfc5832 + * http://tools.ietf.org/html/rfc7091 + * http://tools.ietf.org/html/rfc4357 + * + */ // + +var root = {}; +var rootCrypto = crypto; +var CryptoOperationData = ArrayBuffer; + +var OperationError = Error, + DataError = Error, + NotSupportedError = Error; + +// Predefined named curve collection +var ECGostParams = { + 'S-256-TEST': { + a: 7, + b: '0x5FBFF498AA938CE739B8E022FBAFEF40563F6E6A3472FC2A514C0CE9DAE23B7E', + p: '0x8000000000000000000000000000000000000000000000000000000000000431', + q: '0x8000000000000000000000000000000150FE8A1892976154C59CFC193ACCF5B3', + x: 2, + y: '0x8E2A8A0E65147D4BD6316030E16D19C85C97F0A9CA267122B96ABBCEA7E8FC8' + }, + 'S-256-A': { + a: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94', + b: 166, + p: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97', + q: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893', + x: 1, + y: '0x8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14' + }, + 'S-256-B': { + a: '0x8000000000000000000000000000000000000000000000000000000000000C96', + b: '0x3E1AF419A269A5F866A7D3C25C3DF80AE979259373FF2B182F49D4CE7E1BBC8B', + p: '0x8000000000000000000000000000000000000000000000000000000000000C99', + q: '0x800000000000000000000000000000015F700CFFF1A624E5E497161BCC8A198F', + x: 1, + y: '0x3FA8124359F96680B83D1C3EB2C070E5C545C9858D03ECFB744BF8D717717EFC' + }, + 'S-256-C': { + a: '0x9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D7598', + b: 32858, + p: '0x9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D759B', + q: '0x9B9F605F5A858107AB1EC85E6B41C8AA582CA3511EDDFB74F02F3A6598980BB9', + x: 0, + y: '0x41ECE55743711A8C3CBF3783CD08C0EE4D4DC440D4641A8F366E550DFDB3BB67' + }, + 'P-256': { + p: '0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF', + a: '0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC', + b: '0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B', + x: '0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296', + y: '0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5', + q: '0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551' + }, + 'T-512-TEST': { + a: 7, + b: '0x1CFF0806A31116DA29D8CFA54E57EB748BC5F377E49400FDD788B649ECA1AC4361834013B2AD7322480A89CA58E0CF74BC9E540C2ADD6897FAD0A3084F302ADC', + p: '0x4531ACD1FE0023C7550D267B6B2FEE80922B14B2FFB90F04D4EB7C09B5D2D15DF1D852741AF4704A0458047E80E4546D35B8336FAC224DD81664BBF528BE6373', + q: '0x4531ACD1FE0023C7550D267B6B2FEE80922B14B2FFB90F04D4EB7C09B5D2D15DA82F2D7ECB1DBAC719905C5EECC423F1D86E25EDBE23C595D644AAF187E6E6DF', + x: '0x24D19CC64572EE30F396BF6EBBFD7A6C5213B3B3D7057CC825F91093A68CD762FD60611262CD838DC6B60AA7EEE804E28BC849977FAC33B4B530F1B120248A9A', + y: '0x2BB312A43BD2CE6E0D020613C857ACDDCFBF061E91E5F2C3F32447C259F39B2C83AB156D77F1496BF7EB3351E1EE4E43DC1A18B91B24640B6DBB92CB1ADD371E' + }, + 'T-512-A': { + p: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7', + a: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4', + b: '0xE8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760', + q: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275', + x: 3, + y: '0x7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4' + }, + 'T-512-B': { + p: '0x8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006F', + a: '0x8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006C', + b: '0x687D1B459DC841457E3E06CF6F5E2517B97C7D614AF138BCBF85DC806C4B289F3E965D2DB1416D217F8B276FAD1AB69C50F78BEE1FA3106EFB8CCBC7C5140116', + q: '0x800000000000000000000000000000000000000000000000000000000000000149A1EC142565A545ACFDB77BD9D40CFA8B996712101BEA0EC6346C54374F25BD', + x: 2, + y: '0x1A8F7EDA389B094C2C071E3647A8940F3C123B697578C213BE6DD9E6C8EC7335DCB228FD1EDF4A39152CBCAAF8C0398828041055F94CEEEC7E21340780FE41BD' + } +}; +ECGostParams['X-256-A'] = ECGostParams['S-256-A']; +ECGostParams['X-256-B'] = ECGostParams['S-256-C']; +ECGostParams['T-256-TEST'] = ECGostParams['S-256-TEST']; +ECGostParams['T-256-A'] = ECGostParams['S-256-A']; +ECGostParams['T-256-B'] = ECGostParams['S-256-B']; +ECGostParams['T-256-C'] = ECGostParams['S-256-C']; + + +var GostParams = { + 'S-TEST': { + modulusLength: 512, // bit length of p (512 or 1024 bits) + p: '0xEE8172AE8996608FB69359B89EB82A69854510E2977A4D63BC97322CE5DC3386EA0A12B343E9190F23177539845839786BB0C345D165976EF2195EC9B1C379E3', + q: '0x98915E7EC8265EDFCDA31E88F24809DDB064BDC7285DD50D7289F0AC6F49DD2D', + a: '0x9e96031500c8774a869582d4afde2127afad2538b4b6270a6f7c8837b50d50f206755984a49e509304d648be2ab5aab18ebe2cd46ac3d8495b142aa6ce23e21c' + }, + 'S-A': { + modulusLength: 1024, + p: '0xB4E25EFB018E3C8B87505E2A67553C5EDC56C2914B7E4F89D23F03F03377E70A2903489DD60E78418D3D851EDB5317C4871E40B04228C3B7902963C4B7D85D52B9AA88F2AFDBEB28DA8869D6DF846A1D98924E925561BD69300B9DDD05D247B5922D967CBB02671881C57D10E5EF72D3E6DAD4223DC82AA1F7D0294651A480DF', + q: '0x972432A437178B30BD96195B773789AB2FFF15594B176DD175B63256EE5AF2CF', + a: '0x8FD36731237654BBE41F5F1F8453E71CA414FFC22C25D915309E5D2E62A2A26C7111F3FC79568DAFA028042FE1A52A0489805C0DE9A1A469C844C7CABBEE625C3078888C1D85EEA883F1AD5BC4E6776E8E1A0750912DF64F79956499F1E182475B0B60E2632ADCD8CF94E9C54FD1F3B109D81F00BF2AB8CB862ADF7D40B9369A' + }, + 'S-B': { + modulusLength: 1024, + p: '0xC6971FC57524B30C9018C5E621DE15499736854F56A6F8AEE65A7A404632B1BCF0349FFCAFCB0A103177971FC1612ADCDB8C8CC938C70225C8FD12AFF01B1D064E0AD6FDE6AB9159166CB9F2FC171D92F0CC7B6A6B2CD7FA342ACBE2C9315A42D576B1ECCE77A963157F3D0BD96A8EB0B0F3502AD238101B05116334F1E5B7AB', + q: '0xB09D634C10899CD7D4C3A7657403E05810B07C61A688BAB2C37F475E308B0607', + a: '0x3D26B467D94A3FFC9D71BF8DB8934084137264F3C2E9EB16DCA214B8BC7C872485336744934FD2EF5943F9ED0B745B90AA3EC8D70CDC91682478B664A2E1F8FB56CEF2972FEE7EDB084AF746419B854FAD02CC3E3646FF2E1A18DD4BEB3C44F7F2745588029649674546CC9187C207FB8F2CECE8E2293F68395C4704AF04BAB5' + }, + 'S-C': { + modulusLength: 1024, + p: '0x9D88E6D7FE3313BD2E745C7CDD2AB9EE4AF3C8899E847DE74A33783EA68BC30588BA1F738C6AAF8AB350531F1854C3837CC3C860FFD7E2E106C3F63B3D8A4C034CE73942A6C3D585B599CF695ED7A3C4A93B2B947B7157BB1A1C043AB41EC8566C6145E938A611906DE0D32E562494569D7E999A0DDA5C879BDD91FE124DF1E9', + q: '0xFADD197ABD19A1B4653EECF7ECA4D6A22B1F7F893B641F901641FBB555354FAF', + a: '0x7447ED7156310599070B12609947A5C8C8A8625CF1CF252B407B331F93D639DDD1BA392656DECA992DD035354329A1E95A6E32D6F47882D960B8F10ACAFF796D13CD9611F853DAB6D2623483E46788708493937A1A29442598AEC2E0742022563440FE9C18740ECE6765AC05FAF024A64B026E7E408840819E962E7E5F401AE3' + }, + 'S-D': { + modulusLength: 1024, + p: '0x80F102D32B0FD167D069C27A307ADAD2C466091904DBAA55D5B8CC7026F2F7A1919B890CB652C40E054E1E9306735B43D7B279EDDF9102001CD9E1A831FE8A163EED89AB07CF2ABE8242AC9DEDDDBF98D62CDDD1EA4F5F15D3A42A6677BDD293B24260C0F27C0F1D15948614D567B66FA902BAA11A69AE3BCEADBB83E399C9B5', + q: '0xF0F544C418AAC234F683F033511B65C21651A6078BDA2D69BB9F732867502149', + a: '0x6BCC0B4FADB3889C1E06ADD23CC09B8AB6ECDEDF73F04632595EE4250005D6AF5F5ADE44CB1E26E6263C672347CFA26F9E9393681E6B759733784CDE5DBD9A14A39369DFD99FA85CC0D10241C4010343F34A91393A706CF12677CBFA1F578D6B6CFBE8A1242CFCC94B3B653A476E145E3862C18CC3FED8257CFEF74CDB205BF1' + }, + 'X-A': { + modulusLength: 1024, + p: '0xCA3B3F2EEE9FD46317D49595A9E7518E6C63D8F4EB4D22D10D28AF0B8839F079F8289E603B03530784B9BB5A1E76859E4850C670C7B71C0DF84CA3E0D6C177FE9F78A9D8433230A883CD82A2B2B5C7A3306980278570CDB79BF01074A69C9623348824B0C53791D53C6A78CAB69E1CFB28368611A397F50F541E16DB348DBE5F', + q: '0xCAE4D85F80C147704B0CA48E85FB00A9057AA4ACC44668E17F1996D7152690D9', + a: '0xBE27D652F2F1E339DA734211B85B06AE4DE236AA8FBEEB3F1ADCC52CD43853777E834A6A518138678A8ADBD3A55C70A7EAB1BA7A0719548677AAF4E609FFB47F6B9D7E45B0D06D83D7ADC53310ABD85783E7317F7EC73268B6A9C08D260B85D8485696CA39C17B17F044D1E050489036ABD381C5E6BF82BA352A1AFF136601AF' + }, + 'X-B': { + modulusLength: 1024, + p: '0x9286DBDA91ECCFC3060AA5598318E2A639F5BA90A4CA656157B2673FB191CD0589EE05F4CEF1BD13508408271458C30851CE7A4EF534742BFB11F4743C8F787B11193BA304C0E6BCA25701BF88AF1CB9B8FD4711D89F88E32B37D95316541BF1E5DBB4989B3DF13659B88C0F97A3C1087B9F2D5317D557DCD4AFC6D0A754E279', + q: '0xC966E9B3B8B7CDD82FF0F83AF87036C38F42238EC50A876CD390E43D67B6013F', + a: '0x7E9C3096676F51E3B2F9884CF0AC2156779496F410E049CED7E53D8B7B5B366B1A6008E5196605A55E89C3190DABF80B9F1163C979FCD18328DAE5E9048811B370107BB7715F82091BB9DE0E33EE2FED6255474F8769FCE5EAFAEEF1CB5A32E0D5C6C2F0FC0B3447072947F5B4C387666993A333FC06568E534AD56D2338D729' + }, + 'X-C': { + modulusLength: 1024, + p: '0xB194036ACE14139D36D64295AE6C50FC4B7D65D8B340711366CA93F383653908EE637BE428051D86612670AD7B402C09B820FA77D9DA29C8111A8496DA6C261A53ED252E4D8A69A20376E6ADDB3BDCD331749A491A184B8FDA6D84C31CF05F9119B5ED35246EA4562D85928BA1136A8D0E5A7E5C764BA8902029A1336C631A1D', + q: '0x96120477DF0F3896628E6F4A88D83C93204C210FF262BCCB7DAE450355125259', + a: '0x3F1817052BAA7598FE3E4F4FC5C5F616E122CFF9EBD89EF81DC7CE8BF56CC64B43586C80F1C4F56DD5718FDD76300BE336784259CA25AADE5A483F64C02A20CF4A10F9C189C433DEFE31D263E6C9764660A731ECCAECB74C8279303731E8CF69205BC73E5A70BDF93E5BB681DAB4EEB9C733CAAB2F673C475E0ECA921D29782E' + } +}; // + +/* + * BigInteger arithmetic tools + * optimized release of http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js + * + */ // + +// Bits per one element +var DB = 28, DM = (1 << DB) - 1, DV = 1 << DB, + FV = Math.pow(2, 52), F1 = 52 - DB, F2 = 2 * DB - 52; + +function am(y, i, x, w, j, c, n) { + var xl = x & 0x3fff, xh = x >> 14; + while (--n >= 0) { + var l = y[i] & 0x3fff; + var h = y[i++] >> 14; + var m = xh * l + h * xl; + l = xl * l + ((m & 0x3fff) << 14) + w[j] + c; + c = (l >> 28) + (m >> 14) + xh * h; + w[j++] = l & 0xfffffff; + } + return c; +} + +function nbi(words) { + var r = new Array(Math.ceil(words)); + r.s = 0; + r.t = 0; + return r; +} + +function copyTo(x, r) { + for (var i = x.t - 1; i >= 0; --i) + r[i] = x[i]; + r.t = x.t; + r.s = x.s; + return r; +} + +function copy(x) { + return copyTo(x, nbi(x.t)); +} + +function setInt(x, i) { + x.t = 1; + x.s = (i < 0) ? -1 : 0; + if (i > 0) + x[0] = i; + else if (i < -1) + x[0] = i + DV; + else + x.t = 0; + return x; +} + +function nbv(i) { + var r = nbi(1); + setInt(r, i); + return r; +} + +var ZERO = nbv(0), ONE = nbv(1), THREE = nbv(3); + +function clamp(x) { + var c = x.s & DM; + while (x.t > 0 && x[x.t - 1] === c) + --x.t; + return x; +} + +function subTo(x, a, r) { + var i = 0, c = 0, m = Math.min(a.t, x.t); + while (i < m) { + c += x[i] - a[i]; + r[i++] = c & DM; + c >>= DB; + } + if (a.t < x.t) { + c -= a.s; + while (i < x.t) { + c += x[i]; + r[i++] = c & DM; + c >>= DB; + } + c += x.s; + } + else { + c += x.s; + while (i < a.t) { + c -= a[i]; + r[i++] = c & DM; + c >>= DB; + } + c -= a.s; + } + r.s = (c < 0) ? -1 : 0; + if (c < -1) + r[i++] = DV + c; + else if (c > 0) + r[i++] = c; + r.t = i; + return clamp(r); +} + +function sub(x, y) { + return subTo(x, y, nbi(x.t)); +} + +function addTo(x, a, r) { + var i = 0, c = 0, m = Math.min(a.t, x.t); + while (i < m) { + c += x[i] + a[i]; + r[i++] = c & DM; + c >>= DB; + } + if (a.t < x.t) { + c += a.s; + while (i < x.t) { + c += x[i]; + r[i++] = c & DM; + c >>= DB; + } + c += x.s; + } + else { + c += x.s; + while (i < a.t) { + c += a[i]; + r[i++] = c & DM; + c = c >> DB; + } + c += a.s; + } + r.s = (c < 0) ? -1 : 0; + if (c > 0) + r[i++] = c; + else if (c < -1) + r[i++] = DV + c; + r.t = i; + return clamp(r); +} + +function add(x, y) { + return addTo(x, y, nbi(x.t)); +} + +function negTo(x, r) { + return subTo(ZERO, x, r); +} + +function neg(x) { + return negTo(x, nbi(x.t)); +} + +function absTo(x, r) { + return (x.s < 0) ? negTo(r) : copyTo(r); +} + +function abs(x) { + return (x.s < 0) ? neg(x) : x; +} + +function compare(x, a) { + var r = x.s - a.s; + if (r !== 0) + return r; + var i = x.t; + r = i - a.t; + if (r !== 0) + return (x.s < 0) ? -r : r; + while (--i >= 0) + if ((r = x[i] - a[i]) !== 0) + return r; + return 0; +} + +function equals(x, y) { + return(compare(x, y) === 0); +} + +function min(x, y) { + return(compare(x, y) < 0) ? x : y; +} + +function max(x, y) { + return(compare(x, y) > 0) ? x : y; +} + +function nbits(x) { + var r = 1, t; + if ((t = x >>> 16) !== 0) { + x = t; + r += 16; + } + if ((t = x >> 8) !== 0) { + x = t; + r += 8; + } + if ((t = x >> 4) !== 0) { + x = t; + r += 4; + } + if ((t = x >> 2) !== 0) { + x = t; + r += 2; + } + if ((t = x >> 1) !== 0) { + x = t; + r += 1; + } + return r; +} + +function dshlTo(x, n, r) { + var i; + for (i = x.t - 1; i >= 0; --i) + r[i + n] = x[i]; + for (i = n - 1; i >= 0; --i) + r[i] = 0; + r.t = x.t + n; + r.s = x.s; + return r; +} +function dshrTo(x, n, r) { + for (var i = n; i < x.t; ++i) + r[i - n] = x[i]; + r.t = Math.max(x.t - n, 0); + r.s = x.s; + return r; +} + +function shlTo(x, n, r) { + var bs = n % DB; + var cbs = DB - bs; + var bm = (1 << cbs) - 1; + var ds = Math.floor(n / DB), c = (x.s << bs) & DM, i; + for (i = x.t - 1; i >= 0; --i) { + r[i + ds + 1] = (x[i] >> cbs) | c; + c = (x[i] & bm) << bs; + } + for (i = ds - 1; i >= 0; --i) + r[i] = 0; + r[ds] = c; + r.t = x.t + ds + 1; + r.s = x.s; + return clamp(r); +} + +function shrTo(x, n, r) { + r.s = x.s; + var ds = Math.floor(n / DB); + if (ds >= x.t) { + r.t = 0; + return; + } + var bs = n % DB; + var cbs = DB - bs; + var bm = (1 << bs) - 1; + r[0] = x[ds] >> bs; + for (var i = ds + 1; i < x.t; ++i) { + r[i - ds - 1] |= (x[i] & bm) << cbs; + r[i - ds] = x[i] >> bs; + } + if (bs > 0) + r[x.t - ds - 1] |= (x.s & bm) << cbs; + r.t = x.t - ds; + return clamp(r); +} + +function shl(x, n) { + var r = nbi(x.t); + if (n < 0) + shrTo(x, -n, r); + else + shlTo(x, n, r); + return r; +} + +function shr(x, n) { + var r = nbi(x.t); + if (n < 0) + shlTo(x, -n, r); + else + shrTo(x, n, r); + return r; +} + +function bitLength(x) { + if (x.t <= 0) + return 0; + return DB * (x.t - 1) + nbits(x[x.t - 1] ^ (x.s & DM)); +} + +function mulTo(b, a, r) { + var x = abs(b), y = abs(a); + var i = x.t; + r.t = i + y.t; + while (--i >= 0) + r[i] = 0; + for (i = 0; i < y.t; ++i) + r[i + x.t] = am(x, 0, y[i], r, i, 0, x.t); + r.s = 0; + if (b.s !== a.s) + subTo(ZERO, r, r); + return clamp(r); +} + +function mul(x, y) { + return mulTo(x, y, nbi(x.t + y.t)); +} + +function sqrTo(a, r) { + var x = abs(a); + var i = r.t = 2 * x.t; + while (--i >= 0) + r[i] = 0; + for (i = 0; i < x.t - 1; ++i) { + var c = am(x, i, x[i], r, 2 * i, 0, 1); + if ((r[i + x.t] += am(x, i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) { + r[i + x.t] -= x.DV; + r[i + x.t + 1] = 1; + } + } + if (r.t > 0) + r[r.t - 1] += am(x, i, x[i], r, 2 * i, 0, 1); + r.s = 0; + return clamp(r); +} + +function sqr(a) { + return sqrTo(a, nbi(a.t * 2)); +} + +function divRemTo(n, m, q, r) { + var pm = abs(m); + if (pm.t <= 0) + throw new OperationError('Division by zero'); + var pt = abs(n); + if (pt.t < pm.t) { + if (q) + setInt(q, 0); + if (r) + copyTo(n, r); + return q; + } + if (!r) + r = nbi(m.t); + var y = nbi(m.t), ts = n.s, ms = m.s; + var nsh = DB - nbits(pm[pm.t - 1]); + if (nsh > 0) { + shlTo(pm, nsh, y); + shlTo(pt, nsh, r); + } + else { + copyTo(pm, y); + copyTo(pt, r); + } + var ys = y.t; + var y0 = y[ys - 1]; + if (y0 === 0) + return q; + var yt = y0 * (1 << F1) + ((ys > 1) ? y[ys - 2] >> F2 : 0); + var d1 = FV / yt, d2 = (1 << F1) / yt, e = 1 << F2; + var i = r.t, j = i - ys, t = !q ? nbi(Math.max(n.t - m.t, 1)) : q; + dshlTo(y, j, t); + if (compare(r, t) >= 0) { + r[r.t++] = 1; + subTo(r, t, r); + } + dshlTo(ONE, ys, t); + subTo(t, y, y); + while (y.t < ys) + y[y.t++] = 0; + while (--j >= 0) { + var qd = (r[--i] === y0) ? DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2); + if ((r[i] += am(y, 0, qd, r, j, 0, ys)) < qd) { + dshlTo(y, j, t); + subTo(r, t, r); + while (r[i] < --qd) + subTo(r, t, r); + } + } + if (q) { + dshrTo(r, ys, q); + if (ts !== ms) + subTo(ZERO, q, q); + } + r.t = ys; + clamp(r); + if (nsh > 0) + shrTo(r, nsh, r); + if (ts < 0) + subTo(ZERO, r, r); + return q; +} + +function modTo(b, a, r) { + divRemTo(abs(b), a, null, r); + if (b.s < 0 && compare(r, ZERO) > 0) + subTo(a, r, r); + return r; +} + +function mod(b, a) { + return modTo(b, a, nbi(a.t)); +} + +function div(b, a) { + return divRemTo(b, a, nbi(Math.max(b.t - a.t, 1)), null); +} + +function isEven(x) { + + return ((x.t > 0) ? (x[0] & 1) : x.s) === 0; +} + +function isZero(x) { + return equals(x, ZERO); +} + +function sig(x) { + if (x.s < 0) + return -1; + else if (x.t <= 0 || (x.t === 1 && x[0] <= 0)) + return 0; + else + return 1; +} + +function invMod(x, m) { + var ac = isEven(m); + if ((isEven(x) && ac) || sig(m) === 0) + return ZERO; + var u = copy(m), v = copy(x); + var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); + while (sig(u) !== 0) { + while (isEven(u)) { + shrTo(u, 1, u); + if (ac) { + if (!isEven(a) || !isEven(b)) { + addTo(a, x, a); + subTo(b, m, b); + } + shrTo(a, 1, a); + } + else if (!isEven(b)) + subTo(b, m, b); + shrTo(b, 1, b); + } + while (isEven(v)) { + shrTo(v, 1, v); + if (ac) { + if (!isEven(c) || !isEven(d)) { + addTo(c, x, c); + subTo(d, m, d); + } + shrTo(c, 1, c); + } + else if (!isEven(d)) + subTo(d, m, d); + shrTo(d, 1, d); + } + if (compare(u, v) >= 0) { + subTo(u, v, u); + if (ac) + subTo(a, c, a); + subTo(b, d, b); + } + else { + subTo(v, u, v); + if (ac) + subTo(c, a, c); + subTo(d, b, d); + } + } + if (compare(v, ONE) !== 0) + return ZERO; + if (compare(d, m) >= 0) + return subtract(d, m); + if (sig(d) < 0) + addTo(d, m, d); + else + return d; + if (sig(d) < 0) + return add(d, m); + else + return d; +} + +function testBit(x, n) { + var j = Math.floor(n / DB); + if (j >= x.t) + return (x.s !== 0); + return ((x[j] & (1 << (n % DB))) !== 0); +} + +function nothing(x) { + return x; +} + +function extend(c, o) { + for (var i in o) + c.prototype[i] = o[i]; +} // + +/* + * Classic, Barret, Mongomery reductions, optimized ExpMod algorithms + * optimized release of http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn2.js + * + */ // + +// Classic reduction +var Classic = function (m) { + this.m = m; +}; + +extend(Classic, { + convert: function (x) { + if (x.s < 0 || compare(x, this.m) >= 0) + return mod(x, this.m); + else + return x; + }, + revert: nothing, + reduce: function (x) { + modTo(x, this.m, x); + }, + sqrTo: function (x, r) { + sqrTo(x, r); + this.reduce(r); + }, + mulTo: function (x, y, r) { + mulTo(x, y, r); + this.reduce(r); + } +}); + +function invDig(a) { + if (a.t < 1) + return 0; + var x = a[0]; + if ((x & 1) === 0) + return 0; + var y = x & 3; + y = (y * (2 - (x & 0xf) * y)) & 0xf; + y = (y * (2 - (x & 0xff) * y)) & 0xff; + y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; + y = (y * (2 - x * y % DV)) % DV; + return (y > 0) ? DV - y : -y; +} + +// Montgomery reduction +var Montgomery = function (m) { + this.m = m; + this.mp = invDig(m); + this.mpl = this.mp & 0x7fff; + this.mph = this.mp >> 15; + this.um = (1 << (DB - 15)) - 1; + this.mt2 = 2 * m.t; +}; + +extend(Montgomery, { + // xR mod m + convert: function (x) { + var r = nbi(x.t); + dshlTo(abs(x), this.m.t, r); + divRemTo(r, this.m, null, r); + if (x.s < 0 && compare(r, ZERO) > 0) + subTo(this.m, r, r); + return r; + }, + // x/R mod m + revert: function (x) { + var r = nbi(x.t); + copyTo(x, r); + this.reduce(r); + return r; + }, + // x = x/R mod m (HAC 14.32) + reduce: function (x) { + while (x.t <= this.mt2) + x[x.t++] = 0; + for (var i = 0; i < this.m.t; ++i) { + var j = x[i] & 0x7fff; + var u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & DM; + j = i + this.m.t; + x[j] += am(this.m, 0, u0, x, i, 0, this.m.t); + while (x[j] >= DV) { + x[j] -= DV; + x[++j]++; + } + } + clamp(x); + dshrTo(x, this.m.t, x); + if (compare(x, this.m) >= 0) + subTo(x, this.m, x); + }, + // r = "x^2/R mod m"; x != r + sqrTo: function (x, r) { + sqrTo(x, r); + this.reduce(r); + }, + // r = "xy/R mod m"; x,y != r + mulTo: function (x, y, r) { + mulTo(x, y, r); + this.reduce(r); + } +}); + +function dAddOffset(x, n, w) { + if (n === 0) + return; + while (x.t <= w) + x[x.t++] = 0; + x[w] += n; + while (x[w] >= DV) { + x[w] -= DV; + if (++w >= x.t) + x[x.t++] = 0; + ++x[w]; + } +} + +function mulLowerTo(x, a, n, r) { + var i = Math.min(x.t + a.t, n); + r.s = 0; // assumes a,x >= 0 + r.t = i; + while (i > 0) + r[--i] = 0; + var j; + for (j = r.t - x.t; i < j; ++i) + r[i + x.t] = am(x, 0, a[i], r, i, 0, x.t); + for (j = Math.min(a.t, n); i < j; ++i) + am(x, 0, a[i], r, i, 0, n - i); + return clamp(r); +} + +function mulUpperTo(x, a, n, r) { + --n; + var i = r.t = x.t + a.t - n; + r.s = 0; // assumes a,x >= 0 + while (--i >= 0) + r[i] = 0; + for (i = Math.max(n - x.t, 0); i < a.t; ++i) + r[x.t + i - n] = am(x, n - i, a[i], r, 0, 0, x.t + i - n); + clamp(r); + return dshrTo(r, 1, r); +} + +// Barrett modular reduction +function Barrett(m) { + // setup Barrett + this.r2 = nbi(2 * m.t); + this.q3 = nbi(2 * m.t); + dshlTo(ONE, 2 * m.t, this.r2); + this.mu = div(this.r2, m); + this.m = m; +} + +extend(Barrett, { + convert: function (x) { + if (x.s < 0 || x.t > 2 * this.m.t) + return mod(x, this.m); + else if (compare(x, this.m) < 0) + return x; + else { + var r = nbi(x.t); + copyTo(x, r); + this.reduce(r); + return r; + } + }, + revert: function (x) { + return x; + }, + // x = x mod m (HAC 14.42) + reduce: function (x) { + dshrTo(x, this.m.t - 1, this.r2); + if (x.t > this.m.t + 1) { + x.t = this.m.t + 1; + clamp(x); + } + mulUpperTo(this.mu, this.r2, this.m.t + 1, this.q3); + mulLowerTo(this.m, this.q3, this.m.t + 1, this.r2); + while (compare(x, this.r2) < 0) + dAddOffset(x, 1, this.m.t + 1); + subTo(x, this.r2, x); + while (compare(x, this.m) >= 0) + subTo(x, this.m, x); + }, + // r = x^2 mod m; x != r + sqrTo: function (x, r) { + sqrTo(x, r); + this.reduce(r); + }, + // r = x*y mod m; x,y != r + mulTo: function (x, y, r) { + mulTo(x, y, r); + this.reduce(r); + } + +}); + +// x^e % m (HAC 14.85) +function expMod(x, e, m) { + var i = bitLength(e), k, r = nbv(1), z; + if (i <= 0) + return r; + else if (i < 18) + k = 1; + else if (i < 48) + k = 3; + else if (i < 144) + k = 4; + else if (i < 768) + k = 5; + else + k = 6; + if (i < 8) + z = new Classic(m); + else if (isEven(m)) + z = new Barrett(m); + else + z = new Montgomery(m); + + // precomputation + var g = new Array(), n = 3, k1 = k - 1, km = (1 << k) - 1; + g[1] = z.convert(x); + if (k > 1) { + var g2 = nbi(m.t * 2); + z.sqrTo(g[1], g2); + while (n <= km) { + g[n] = nbi(m.t * 2); + z.mulTo(g2, g[n - 2], g[n]); + n += 2; + } + } + + var j = e.t - 1, w, is1 = true, r2 = nbi(m.t * 2), t; + i = nbits(e[j]) - 1; + while (j >= 0) { + if (i >= k1) + w = (e[j] >> (i - k1)) & km; + else { + w = (e[j] & ((1 << (i + 1)) - 1)) << (k1 - i); + if (j > 0) + w |= e[j - 1] >> (DB + i - k1); + } + + n = k; + while ((w & 1) == 0) { + w >>= 1; + --n; + } + if ((i -= n) < 0) { + i += DB; + --j; + } + if (is1) { // ret == 1, don't bother squaring or multiplying it + copyTo(g[w], r); + is1 = false; + } + else { + while (n > 1) { + z.sqrTo(r, r2); + z.sqrTo(r2, r); + n -= 2; + } + if (n > 0) + z.sqrTo(r, r2); + else { + t = r; + r = r2; + r2 = t; + } + z.mulTo(r2, g[w], r); + } + while (j >= 0 && (e[j] & (1 << i)) == 0) { + z.sqrTo(r, r2); + t = r; + r = r2; + r2 = t; + if (--i < 0) { + i = DB - 1; + --j; + } + } + } + return z.revert(r); +} // + +/* + * EC Field Elements, Points, Curves + * optimized release of http://www-cs-students.stanford.edu/~tjw/jsbn/ec.js + * + */ // + +// EC Field Elemets +function newFE(a, x) { + a.r.reduce(x); + x.q = a.q; + x.r = a.r; + return x; +} + +function copyFE(a, x) { + x.q = a.q; + x.r = a.r; + return x; +} + +function negFE(a) { + return copyFE(a, sub(a.q, a)); +} + +function addFE(a, b) { + var r = add(a, b); + if (compare(r, a.q) > 0) + subTo(r, a.q, r); + return copyFE(a, r); +} + +function subFE(a, b) { + var r = sub(a, b); + if (r.s < 0) + addTo(a.q, r, r); + return copyFE(a, r); +} + +function mulFE(a, b) { + return newFE(a, mul(a, b)); +} + +function sqrFE(a) { + return newFE(a, sqr(a)); +} + +function shlFE(a, i) { + return newFE(a, shl(a, i)); +} + +function invFE(a) { + return copyFE(a, invMod(a, a.q)); +} + +// EC Points +function newEC(curve, x, y, z) { + return { + curve: curve, + x: x, + y: y, + z: z || newFE(curve, ONE) + }; +} + +function getX(point) { + if (!point.zinv) + point.zinv = invFE(point.z); + return mulFE(point.x, point.zinv); +} + +function getY(point) { + if (!point.zinv) + point.zinv = invFE(point.z); + return mulFE(point.y, point.zinv); +} + +function isInfinity(a) { + if ((!a.x) && (!a.y)) + return true; + return isZero(a.z) && !isZero(a.y); +} + +function getInfinity(a) { + return a.curve.infinity; +} + +function equalsEC(a, b) { + if (a === b) + return true; + if (isInfinity(a)) + return isInfinity(b); + if (isInfinity(b)) + return isInfinity(a); + var u, v; + // u = Y2 * Z1 - Y1 * Z2 + u = subFE(mulFE(b.y, a.z), mulFE(a.y, b.z)); + if (!isZero(u)) + return false; + // v = X2 * Z1 - X1 * Z2 + v = subFE(mulFE(b.x, a.z), mulFE(a.x, b.z)); + return isZero(v); +} + +function negEC(a) { + return newEC(a.curve, a.x, negFE(a.y), a.z); +} + +function addEC(a, b) { + if (isInfinity(a)) + return b; + if (isInfinity(b)) + return a; + + // u = Y2 * Z1 - Y1 * Z2 + var u = subFE(mulFE(b.y, a.z), mulFE(a.y, b.z)); + // v = X2 * Z1 - X1 * Z2 + var v = subFE(mulFE(b.x, a.z), mulFE(a.x, b.z)); + + if (isZero(v)) { + if (isZero(u)) { + return twiceEC(a); // a == b, so double + } + return getInfinity(a); // a = -b, so infinity + } + + var x1 = a.x; + var y1 = a.y; + + var v2 = sqrFE(v); + var v3 = mulFE(v2, v); + var x1v2 = mulFE(x1, v2); + var zu2 = mulFE(sqrFE(u), a.z); + + // x3 = v * (z2 * (z1 * u^2 - 2 * x1 * v^2) - v^3) + var x3 = mulFE(subFE(mulFE(subFE(zu2, shlFE(x1v2, 1)), b.z), v3), v); + // y3 = z2 * (3 * x1 * u * v^2 - y1 * v^3 - z1 * u^3) + u * v^3 + var y3 = addFE(mulFE(subFE(subFE(mulFE(mulFE(x1v2, THREE), u), mulFE(y1, v3)), mulFE(zu2, u)), b.z), mulFE(u, v3)); + // z3 = v^3 * z1 * z2 + var z3 = mulFE(mulFE(v3, a.z), b.z); + + return newEC(a.curve, x3, y3, z3); +} + +function twiceEC(b) { + if (isInfinity(b)) + return b; + if (sig(b.y) === 0) + return getInfinity(b); + + var x1 = b.x; + var y1 = b.y; + + var y1z1 = mulFE(y1, b.z); + var y1sqz1 = mulFE(y1z1, y1); + var a = b.curve.a; + + // w = 3 * x1^2 + a * z1^2 + var w = mulFE(sqrFE(x1), THREE); + if (!isZero(a)) { + w = addFE(w, mulFE(sqrFE(b.z), a)); + } + + // x3 = 2 * y1 * z1 * (w^2 - 8 * x1 * y1^2 * z1) + var x3 = mulFE(shlFE(subFE(sqrFE(w), mulFE(shlFE(x1, 3), y1sqz1)), 1), y1z1); + // y3 = 4 * y1^2 * z1 * (3 * w * x1 - 2 * y1^2 * z1) - w^3 + var y3 = subFE(mulFE(shlFE(subFE(mulFE(mulFE(w, THREE), x1), shlFE(y1sqz1, 1)), 2), y1sqz1), mulFE(sqrFE(w), w)); + // z3 = 8 * (y1 * z1)^3 + var z3 = shlFE(mulFE(sqrFE(y1z1), y1z1), 3); + + return newEC(b.curve, x3, y3, z3); +} + +// Simple NAF (Non-Adjacent Form) multiplication algorithm +function mulEC(a, k) { + if (isInfinity(a)) + return a; + if (sig(k) === 0) + return getInfinity(a); + + var e = k; + var h = mul(e, THREE); + + var neg = negEC(a); + var R = a; + + var i; + for (i = bitLength(h) - 2; i > 0; --i) { + R = twiceEC(R); + + var hBit = testBit(h, i); + var eBit = testBit(e, i); + + if (hBit !== eBit) { + R = addEC(R, hBit ? a : neg); + } + } + + return R; +} + +function mul2AndAddEC(a, k) { + var nbits = bitLength(k); + var R = a, + Q = getInfinity(a); + + for (var i = 0; i < nbits - 1; i++) { + if (testBit(k, i) === 1) + Q = addEC(Q, R); + + R = twiceEC(R); + } + + if (testBit(k, nbits - 1) === 1) + Q = addEC(Q, R); + + return Q; +} + +// Compute a*j + x*k (simultaneous multiplication) +function mulTwoEC(a, j, x, k) { + var i; + if (bitLength(j) > bitLength(k)) + i = bitLength(j) - 1; + else + i = bitLength(k) - 1; + + var R = getInfinity(a); + var both = addEC(a, x); + while (i >= 0) { + R = twiceEC(R); + if (testBit(j, i)) { + if (testBit(k, i)) { + R = addEC(R, both); + } + else { + R = addEC(R, a); + } + } + else { + if (testBit(k, i)) { + R = addEC(R, x); + } + } + --i; + } + + return R; +} + +// EC Curve +function newCurve(q, a, b) { + var curve = {}; + curve.q = q; + curve.r = new Barrett(q); + curve.a = newFE(curve, a); + curve.b = newFE(curve, b); + curve.infinity = newEC(curve); + return curve; +} // + +/* + * Converion tools (hex, binary) + * + */ // + +function atobi(d) { + var k = 8; + var a = new Uint8Array(d); + var r = nbi(a.length * 8 / DB); + r.t = 0; + r.s = 0; + var sh = 0; + for (var i = 0, n = a.length; i < n; i++) { + var x = a[i]; + if (sh === 0) + r[r.t++] = x; + else if (sh + k > DB) { + r[r.t - 1] |= (x & ((1 << (DB - sh)) - 1)) << sh; + r[r.t++] = (x >> (DB - sh)); + } + else + r[r.t - 1] |= x << sh; + sh += k; + if (sh >= DB) + sh -= DB; + } + return clamp(r); +} + +function bitoa(s, bitLength) { + var k = 8; + var km = (1 << k) - 1, d, m = false, r = [], i = s.t; + var p = DB - (i * DB) % k; + if (i-- > 0) { + if (p < DB && (d = s[i] >> p) > 0) { + m = true; + r.push(d); + } + while (i >= 0) { + if (p < k) { + d = (s[i] & ((1 << p) - 1)) << (k - p); + d |= s[--i] >> (p += DB - k); + } + else { + d = (s[i] >> (p -= k)) & km; + if (p <= 0) { + p += DB; + --i; + } + } + if (d > 0) + m = true; + if (m) + r.push(d); + } + } + var r8 = new Uint8Array(bitLength ? bitLength / 8 : r.length); + if (m) + r8.set(r.reverse()); + return r8.buffer; +} + + +function htobi(s) { + if (typeof s === 'number' || s instanceof Number) + return nbv(s); + s = s.replace(/[^\-A-Fa-f0-9]/g, ''); + if (!s) + s = '0'; + var k = 4; + var r = nbi(s.length / 7); + var i = s.length, mi = false, sh = 0; + while (--i >= 0) { + var c = s.charAt(i); + if (c === '-') { + mi = true; + continue; + } + var x = parseInt(s.charAt(i), 16); + mi = false; + if (sh === 0) + r[r.t++] = x; + else if (sh + k > DB) { + r[r.t - 1] |= (x & ((1 << (DB - sh)) - 1)) << sh; + r[r.t++] = (x >> (DB - sh)); + } + else + r[r.t - 1] |= x << sh; + sh += k; + if (sh >= DB) + sh -= DB; + } + if (mi) + subTo(ZERO, r, r); + return clamp(r); +} + +function bitoh(x) { + if (x.s < 0) + return "-" + bitoh(negTo(x, nbi(x.t))); + var k = 4; + var km = (1 << k) - 1, d, m = false, r = "", i = x.t; + var p = DB - (i * DB) % k; + if (i-- > 0) { + if (p < DB && (d = x[i] >> p) > 0) { + m = true; + r = d.toString(16); + } + while (i >= 0) { + if (p < k) { + d = (x[i] & ((1 << p) - 1)) << (k - p); + d |= x[--i] >> (p += DB - k); + } + else { + d = (x[i] >> (p -= k)) & km; + if (p <= 0) { + p += DB; + --i; + } + } + if (d > 0) + m = true; + if (m) + r += d.toString(16); + } + } + return "0x" + (m ? r : "0"); +} + +// biginteger to big-endian integer bytearray +function bitoi(s) { + var i = s.t, r = []; + r[0] = s.s; + var p = DB - (i * DB) % 8, d, k = 0; + if (i-- > 0) { + if (p < DB && (d = s[i] >> p) !== (s.s & DM) >> p) + r[k++] = d | (s.s << (DB - p)); + while (i >= 0) { + if (p < 8) { + d = (s[i] & ((1 << p) - 1)) << (8 - p); + d |= s[--i] >> (p += DB - 8); + } + else { + d = (s[i] >> (p -= 8)) & 0xff; + if (p <= 0) { + p += DB; + --i; + } + } + if ((d & 0x80) !== 0) + d |= -256; + if (k === 0 && (s.s & 0x80) !== (d & 0x80)) + ++k; + if (k > 0 || d !== s.s) + r[k++] = d; + } + } + return new Uint8Array(r).buffer; +} + +// big-endian integer bytearray to biginteger +function itobi(d) { + var k = 8, s = new Uint8Array(d), + r = nbi(s.length / 7); + r.t = 0; + r.s = 0; + var i = s.length, sh = 0; + while (--i >= 0) { + var x = s[i] & 0xff; + if (sh === 0) + r[r.t++] = x; + else if (sh + k > DB) { + r[r.t - 1] |= (x & ((1 << (DB - sh)) - 1)) << sh; + r[r.t++] = (x >> (DB - sh)); + } + else + r[r.t - 1] |= x << sh; + sh += k; + if (sh >= DB) + sh -= DB; + } + if ((s[0] & 0x80) !== 0) { + r.s = -1; + if (sh > 0) + r[r.t - 1] |= ((1 << (DB - sh)) - 1) << sh; + } + return clamp(r); +} + + +// Swap bytes in buffer +function swap(s) { + var src = new Uint8Array(s), + dst = new Uint8Array(src.length); + for (var i = 0, n = src.length; i < n; i++) + dst[n - i - 1] = src[i]; + return dst.buffer; +} + +// Calculate hash of data +function hash(d) { + if (this.hash) + d = this.hash.digest(d); + // Swap hash for SignalCom + if (this.procreator === 'SC' || + (this.procreator === 'VN' && this.hash.version === 2012)) + d = swap(d); + return d; +} + +// Check buffer +function buffer(d) { + if (d instanceof CryptoOperationData) + return d; + else if (d && d.buffer && d.buffer instanceof CryptoOperationData) + return d.byteOffset === 0 && d.byteLength === d.buffer.byteLength ? + d.buffer : new Uint8Array(new Uint8Array(d, d.byteOffset, d.byteLength)).buffer; + else + throw new DataError('CryptoOperationData or CryptoOperationDataView required'); +} + +// Check double buffer +function to2(d) { + var b = buffer(d); + if (b.byteLength % 2 > 0) + throw new DataError('Buffer length must be even'); + var n = b.byteLength / 2; + return [atobi(new Uint8Array(b, 0, n)), atobi(new Uint8Array(b, n, n))]; +} + +function from2(x, y, bitLength) { + var a = bitoa(x, bitLength), + b = bitoa(y, bitLength), + d = new Uint8Array(a.byteLength + b.byteLength); + d.set(new Uint8Array(a)); + d.set(new Uint8Array(b), a.byteLength); + return d.buffer; +} + +function getSeed(length) { + GostRandom = GostRandom || root.GostRandom; + var randomSource = GostRandom ? new (GostRandom || root.GostRandom) : rootCrypto; + if (randomSource.getRandomValues) { + var d = new Uint8Array(Math.ceil(length / 8)); + randomSource.getRandomValues(d); + return d; + } else + throw new NotSupportedError('Random generator not found'); +} // + +/** + * Algorithm name GOST R 34.10

+ * + * The sign method returns sign data generated with the supplied privateKey.
+ * + * @memberOf GostSign + * @method sign + * @instance + * @param {(CryptoOperationData|TypedArray)} privateKey Private key + * @param {(CryptoOperationData|TypedArray)} data Data + * @returns {CryptoOperationData} Signature + */ +function sign(privateKey, data) // +{ + + // Stage 1 + var b = buffer(data); + var alpha = atobi(hash.call(this, b)); + + var q = this.q; + var x = mod(atobi(buffer(privateKey)), q); + + // Stage 2 + var e = mod(alpha, q); + if (isZero(e)) + e = ONE; + + var s = ZERO; + while (isZero(s)) { + var r = ZERO; + while (isZero(r)) { + + // Stage 3 + var k = mod(atobi(this.ukm || + getSeed(this.bitLength)), q); // pseudo random 0 < k < q + // Stage 4 + if (this.curve) { + // Gost R 34.10-2001 || Gost R 34.10-2012 + var P = this.P; + var C = mulEC(P, k); + r = mod(getX(C), q); + } else { + // Gost R 34.10-94 + var p = this.p, a = this.a; + r = mod(expMod(a, k, p), q); + } + } + // Stage 5 + s = mod(add(mul(r, x), mul(k, e)), q); + } + // Stage 6 + // console.log('s', bitoh(s)); + // console.log('r', bitoh(r)); + var zetta; + // Integer structure for SignalCom algorithm + if (this.procreator === 'SC') { + zetta = { + r: bitoh(r), + s: bitoh(s) + }; + } else { + zetta = from2(r, s, this.bitLength); + // Swap bytes for CryptoPro algorithm + if (this.procreator === 'CP' || this.procreator === 'VN') + zetta = swap(zetta); + } + return zetta; +} // + +/** + * Algorithm name GOST R 34.10

+ * + * The verify method returns signature verification for the supplied publicKey.
+ * + * @memberOf GostSign + * @method sign + * @instance + * @param {(CryptoOperationData|TypedArray)} publicKey Public key + * @param {(CryptoOperationData|TypedArray)} signature Signature + * @param {(CryptoOperationData|TypedArray)} data Data + * @returns {boolean} Signature verified = true + */ +function verify(publicKey, signature, data) // +{ + + // Stage 1 + var q = this.q; + var r, s; + // Ready int for SignalCom algorithm + if (this.procreator === 'SC') { + r = htobi(signature.r); + s = htobi(signature.s); + } else { + if (this.procreator === 'CP' || this.procreator === 'VN') + signature = swap(signature); + var zetta = to2(signature); + // Swap bytes for CryptoPro algorithm + s = zetta[1]; // first 32 octets contain the big-endian representation of s + r = zetta[0]; // and second 32 octets contain the big-endian representation of r + } + if (compare(r, q) >= 0 || compare(s, q) >= 0) + return false; + // Stage 2 + var b = buffer(data); + var alpha = atobi(hash.call(this, b)); + // Stage 3 + var e = mod(alpha, q); + if (isZero(e) === 0) + e = ONE; + // Stage 4 + var v = invMod(e, q); + // Stage 5 + var z1 = mod(mul(s, v), q); + var z2 = sub(q, mod(mul(r, v), q)); + // Stage 6 + if (this.curve) { + // Gost R 34.10-2001 || Gost R 34.10-2012 + var k2 = to2(publicKey), + curve = this.curve, + P = this.P, + x = newFE(curve, k2[0]), // first 32 octets contain the little-endian representation of x + y = newFE(curve, k2[1]), // and second 32 octets contain the little-endian representation of y. + Q = new newEC(curve, x, y); // This corresponds to the binary representation of (256||256) + var C = mulTwoEC(P, z1, Q, z2); + var R = mod(getX(C), q); + } else { + // Gost R 34.10-94 + var p = this.p, a = this.a; + var y = atobi(publicKey); + var R = mod(mod(mul(expMod(a, z1, p), expMod(y, z2, p)), p), q); + } + // Stage 7 + return (compare(R, r) === 0); +} // + +/** + * Algorithm name GOST R 34.10

+ * + * The generateKey method returns a new generated key pair using the specified + * AlgorithmIdentifier. + * + * @memberOf GostSign + * @method generateKey + * @instance + * @returns {Object} Object with two CryptoOperationData members: privateKey and publicKey + */ +function generateKey() // +{ + var curve = this.curve; + if (curve) { + + var Q = curve.infinity; + while (isInfinity(Q)) { + + // Generate random private key + var d = ZERO; + if (this.ukm) { + d = atobi(this.ukm); + } else { + while (isZero(d)) + d = mod(atobi(getSeed(this.bitLength)), this.q); // 0 < d < q + } + + // Calculate public key + Q = mulEC(this.P, d); + var x = getX(Q), y = getY(Q); + // console.log('d', bitoh(d)); + // console.log('x', bitoh(x)); + // console.log('y', bitoh(y)); + } + + // Return result + return { + privateKey: bitoa(d, this.bitLength), + publicKey: from2(x, y, this.bitLength) // This corresponds to the binary representation of (256||256) + }; + + } else + throw new NotSupportedError('Key generation for GOST R 34.10-94 not supported'); +} // + +/** + * Algorithm name GOST R 34.10 mode MASK

+ * + * The generateMaskKey method returns a new generated key mask using for wrapping. + * + * @memberOf GostSign + * @method generateMaskKey + * @instance + * @returns {Object} Object with two CryptoOperationData members: privateKey and publicKey + */ +function generateMaskKey() // +{ + var curve = this.curve; + if (curve) { + // Generate random private key + var d = ZERO; + while (isZero(d)) + d = mod(atobi(getSeed(this.bitLength)), this.q); // 0 < d < q + + // Return result + return bitoa(d, this.bitLength); + } else + throw new NotSupportedError('Key generation for GOST R 34.10-94 not supported'); +} // + +/** + * Algorithm name GOST R 34.10

+ * + * Unwrap private key from private key and ukm (mask) + * + * @memberOf GostSign + * @method unwrap + * @instance + * @param {(CryptoOperationData|TypedArray)} baseKey Unwrapping key + * @param {(CryptoOperationData|TypedArray)} data Wrapped key + * @returns {Object} CryptoOperationData unwrapped privateKey + */ +function unwrapKey(baseKey, data) // +{ + var curve = this.curve; + if (curve) { + var q = this.q; + var x = mod(atobi(buffer(data)), q); + var y = mod(atobi(buffer(baseKey)), q); + var z = this.procreator === 'VN' ? mod(mul(x, y), q) : mod(mul(x, invMod(y, q)), q); + return bitoa(z); + } else + throw new NotSupportedError('Key wrapping GOST R 34.10-94 not supported'); +} // + +/** + * Algorithm name GOST R 34.10

+ * + * Wrap private key with private key and ukm (mask) + * + * @memberOf GostSign + * @method unwrap + * @instance + * @param {(CryptoOperationData|TypedArray)} baseKey Wrapping key + * @param {(CryptoOperationData|TypedArray)} data Key + * @returns {Object} CryptoOperationData unwrapped privateKey + */ +function wrapKey(baseKey, data) // +{ + var curve = this.curve; + if (curve) { + var q = this.q; + var x = mod(atobi(buffer(data)), q); + var y = mod(atobi(buffer(baseKey)), q); + var z = this.procreator === 'VN' ? mod(mul(x, invMod(y, q)), q) : mod(mul(x, y), q); + return bitoa(z); + } else + throw new NotSupportedError('Key wrapping GOST R 34.10-94 not supported'); +} // + +/** + * Algorithm name GOST R 34.10

+ * + * @memberOf GostSign + * @method derive + * @instance + * @private + * @param {CryptoOperationData} baseKey Key for deriviation + * @returns {CryptoOperationData} + */ +function derive(baseKey) // +{ + + var k, ukm = atobi(this.ukm); + var q = this.q; + var x = mod(atobi(buffer(baseKey)), q); + + if (this.curve) { + // 1) Let K(x,y,UKM) = ((UKM*x)(mod q)) . (y.P) (512 bit), where + // x - sender’s private key (256 bit) + // x.P - sender’s public key (512 bit) + // y - recipient’s private key (256 bit) + // y.P - recipient’s public key (512 bit) + // UKM - non-zero integer, produced as in step 2 p. 6.1 [GOSTR341001] + // P - base point on the elliptic curve (two 256-bit coordinates) + // UKM*x - x multiplied by UKM as integers + // x.P - a multiple point + var K = mulEC(this.peer_Q, mod(mul(ukm, x), q)); + k = from2(getX(K), getY(K), // This corresponds to the binary representation of (256||256) + this.bitLength); + } else { + // 1) Let K(x,y) = a^(x*y) (mod p), where + // x - sender’s private key, a^x - sender’s public key + // y - recipient’s private key, a^y - recipient’s public key + // a, p - parameters + var p = this.p, a = this.a; + k = bitoa(expMod(this.peer_y, x, p)); + } + // 2) Calculate a 256-bit hash of K(x,y,UKM): + // KEK(x,y,UKM) = gostSign (K(x,y,UKM) + return hash.call(this, k); +} // + +/** + * Algorithm name GOST R 34.10

+ * + * The deriveBits method returns length bits on baseKey. + * + * @memberOf GostSign + * @method deriveBits + * @instance + * @param {(CryptoOperationData|TypedArray)} baseKey Key for deriviation + * @param {number} length output bit-length + * @returns {CryptoOperationData} result + */ +function deriveBits(baseKey, length) // +{ + if (length < 8 || length > this.bitLength || length % 8 > 0) + throw new DataError('Length must be no more than ' + this.bitLength + ' bits and multiple of 8'); + var n = length / 8, + b = derive.call(this, baseKey), + r = new Uint8Array(n); + + r.set(new Uint8Array(b, 0, n)); + return r.buffer; +} // + +/** + * Algorithm name GOST R 34.10

+ * + * The deriveKey method returns 256 bit Key encryption key on baseKey. + * + * This algorithm creates a key encryption key (KEK) using 64 bit UKM, + * the sender’s private key, and the recipient’s public key (or the + * reverse of the latter pair + * + * @memberOf GostSign + * @method deriveKey + * @instance + * @param {(CryptoOperationData|TypedArray)} baseKey Key for deriviation + * @returns {CryptoOperationData} result + */ +function deriveKey(baseKey) // +{ + var b = derive.call(this, baseKey), + r = new Uint8Array(32); + + r.set(new Uint8Array(b, 0, 32)); + return r.buffer; +} // + + +/** + * Gost R 34.10 universal object

+ * + * References: {@link http://tools.ietf.org/html/rfc6986} and {@link http://tools.ietf.org/html/rfc5831}

+ * + * Normalized algorithm identifier common parameters: + * + *
    + *
  • name Algorithm name 'GOST R 34.10'
  • + *
  • version Algorithm version + *
      + *
    • 1994 - Old-style GOST R 34.10-94 ExpMod algorithm with GOST R 34.11-94 hash
    • + *
    • 2001 - GOST R 34.10-2001 Eliptic curve algorithm with old GOST R 34.11-94 hash
    • + *
    • 2012 - GOST R 34.10-2012 Eliptic curve algorithm with GOST R 34.11-12 hash, default mode
    • + *
    + *
  • + *
  • length Length of hash and signature. Key length == hash length for EC algorithms and 2 * hash length for ExpMod algorithm + *
      + *
    • GOST R 34.10-256 - 256 bits digest, default mode
    • + *
    • GOST R 34.10-512 - 512 bits digest only for GOST R 34.11-2012 hash
    • + *
    + *
  • + *
  • mode Algorithm mode + *
      + *
    • SIGN Digital signature mode (default)
    • + *
    • DH Diffie-Hellman key generation and key agreement mode
    • + *
    + *
  • + *
  • sBox Paramset sBox for GOST 34.11-94. Used only if version = 1994 or 2001
  • + *
+ * + * Supported algorithms, modes and parameters: + * + *
    + *
  • Sign/Verify mode (SIGN)
  • + *
  • DeriveKey/DeriveBits mode (DH) + *
      + *
    • {@link CryptoOperationData} ukm User key material. Default - random generated value
    • + *
    • {@link CryptoOperationData} public The peer's EC public key data
    • + *
    + *
  • + *
  • GenerateKey mode (SIGN and DH) version = 1994 + *
      + *
    • namedParam Paramset for key generation algorithm. If specified no additianal parameters required
    • + *
    + * Additional parameters, if namedParam not specified + *
      + *
    • modulusLength Bit length of p (512 or 1024 bits). Default = 1024
    • + *
    • p {@link CryptoOperationData} Modulus, prime number, 2^(t-1) + *
    • q {@link CryptoOperationData} Order of cyclic group, prime number, 2^254 + *
    • a {@link CryptoOperationData} Generator, integer, 1 + *
    + *
  • + *
  • GenerateKey mode (SIGN and DH) version = 2001 or 2012 + *
      + *
    • namedCurve Paramset for key generation algorithm. If specified no additianal parameters required
    • + *
    + * Additional EC parameters, if namedCurve not specified + *
      + *
    • p {@link CryptoOperationData} Prime number - elliptic curve modulus
    • + *
    • a {@link CryptoOperationData} Coefficients a of the elliptic curve E
    • + *
    • b {@link CryptoOperationData} Coefficients b of the elliptic curve E
    • + *
    • q {@link CryptoOperationData} Prime number - order of cyclic group
    • + *
    • x {@link CryptoOperationData} Base point p x-coordinate
    • + *
    • y {@link CryptoOperationData} Base point p y-coordinate
    • + *
    + *
  • + *
+ * + * @class GostSign + * @param {AlgorithmIndentifier} algorithm + */ +function GostSign(algorithm) // +{ + algorithm = algorithm || {}; + this.name = (algorithm.name || 'GOST R 34.10') + '-' + + ((algorithm.version || 2012) % 100) + '-' + (algorithm.length || 256) + + (((algorithm.mode || 'SIGN') !== 'SIGN') ? '-' + algorithm.mode : '') + + (typeof algorithm.namedParam === 'string' ? '/' + algorithm.namedParam : '') + + (typeof algorithm.namedCurve === 'string' ? '/' + algorithm.namedCurve : '') + + (typeof algorithm.sBox === 'string' ? '/' + algorithm.sBox : ''); + + var version = algorithm.version || 2012; + + // Functions + switch (algorithm.mode || 'SIGN') { + case 'SIGN': + this.sign = sign; + this.verify = verify; + this.generateKey = generateKey; + break; + case 'DH': + this.deriveBits = deriveBits; + this.deriveKey = deriveKey; + this.generateKey = generateKey; + break; + case 'MASK': + this.wrapKey = wrapKey; + this.unwrapKey = unwrapKey; + this.generateKey = generateMaskKey; + break; + } + + // Define parameters + if (version === 1994) { + // Named or parameters algorithm + var param = algorithm.param; + if (!param) + param = GostParams[this.namedParam = (algorithm.namedParam || 'S-A').toUpperCase()]; + this.modulusLength = algorithm.modulusLength || param.modulusLength || 1024; + this.p = htobi(param.p); + this.q = htobi(param.q); + this.a = htobi(param.a); + // Public key for derive + if (algorithm['public']) + this.peer_y = atobi(algorithm['public']); + } else { + // Named or parameters algorithm + var param = algorithm.curve; + if (!param) + param = ECGostParams[this.namedCurve = (algorithm.namedCurve || 'S-256-A').toUpperCase()]; + var curve = this.curve = newCurve(htobi(param.p), htobi(param.a), htobi(param.b)); + this.P = newEC(curve, + newFE(curve, htobi(param.x)), + newFE(curve, htobi(param.y))); + this.q = htobi(param.q); + // Public key for derive + if (algorithm['public']) { + var k2 = to2(algorithm['public']); + this.peer_Q = new newEC(this.curve, // This corresponds to the binary representation of (256||256) + newFE(this.curve, k2[0]), // first 32 octets contain the little-endian representation of x + newFE(this.curve, k2[1])); // and second 32 octets contain the little-endian representation of y. + } + } + + // Check bit length + var hashLen, keyLen; + if (this.curve) { + keyLen = algorithm.length || bitLength(this.q); + if (keyLen > 508 && keyLen <= 512) + keyLen = 512; + else if (keyLen > 254 && keyLen <= 256) + keyLen = 256; + else + throw new NotSupportedError('Support keys only 256 or 512 bits length'); + hashLen = keyLen; + } else { + keyLen = algorithm.modulusLength || bitLength(this.p); + if (keyLen > 1016 && keyLen <= 1024) + keyLen = 1024; + else if (keyLen > 508 && keyLen <= 512) + keyLen = 512; + else + throw new NotSupportedError('Support keys only 512 or 1024 bits length'); + hashLen = 256; + } + this.bitLength = hashLen; + this.keyLength = keyLen; + + // Algorithm proceator for result conversion + this.procreator = algorithm.procreator; + + // Hash function definition + var hash = algorithm.hash; + if (hash) { + if (typeof hash === 'string' || hash instanceof String) + hash = {name: hash}; + if (algorithm.version === 1994 || algorithm.version === 2001) { + hash.version = 1994; + hash.length = 256; + hash.sBox = algorithm.sBox || hash.sBox; + } else { + hash.version = 2012; + hash.length = hashLen; + } + hash.procreator = hash.procreator || algorithm.procreator; + + if (!GostDigest) + GostDigest = root.GostDigest; + if (!GostDigest) + throw new NotSupportedError('Object GostDigest not found'); + + this.hash = new GostDigest(hash); + } + + // Pregenerated seed for key exchange algorithms + if (algorithm.ukm) // Now don't check size + this.ukm = algorithm.ukm; + +} // + +export default GostSign; diff --git a/src/core/vendor/remove-exif.mjs b/src/core/vendor/remove-exif.mjs index 6377c674..6c5c9e53 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"; +import Utils from "../Utils.mjs"; // Param jpeg should be a binaryArray export function removeEXIF(jpeg) { diff --git a/src/node/File.mjs b/src/node/File.mjs new file mode 100644 index 00000000..7a38ba16 --- /dev/null +++ b/src/node/File.mjs @@ -0,0 +1,76 @@ +/** + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import { detectFileType } from "../core/lib/FileType.mjs"; + + +/** + * FileShim + * + * Create a class that behaves like the File object in the Browser so that + * operations that use the File object still work. + * + * File doesn't write to disk, but it would be easy to do so with e.gfs.writeFile. + */ +class File { + + /** + * Constructor + * + * https://w3c.github.io/FileAPI/#file-constructor + * + * @param {String|Array|ArrayBuffer|Buffer|[File]} bits - file content + * @param {String} name (optional) - file name + * @param {Object} stats (optional) - file stats e.g. lastModified + */ + constructor(data, name="", stats={}) { + + if (!Array.isArray(data)) { + data = [data]; + } + + const buffers = data.map((d) => { + if (d instanceof File) { + return Buffer.from(d.data); + } + + if (d instanceof ArrayBuffer) { + return Buffer.from(d); + } + + return Buffer.from(d); + }); + const totalLength = buffers.reduce((p, c) => p + c.length, 0); + this.data = Buffer.concat(buffers, totalLength); + + this.name = name; + this.lastModified = stats.lastModified || Date.now(); + + const types = detectFileType(this.data); + if (types.length) { + this.type = types[0].mime; + } else { + this.type = "application/unknown"; + } + } + + /** + * size property + */ + get size() { + return this.data.length; + } + + /** + * Return lastModified as Date + */ + get lastModifiedDate() { + return new Date(this.lastModified); + } + +} + +export default File; diff --git a/src/node/NodeDish.mjs b/src/node/NodeDish.mjs new file mode 100644 index 00000000..ef9fd11a --- /dev/null +++ b/src/node/NodeDish.mjs @@ -0,0 +1,84 @@ +/** + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import util from "util"; +import Dish from "../core/Dish.mjs"; + +/** + * Subclass of Dish for use in the Node.js environment. + * + * Adds some helper functions and improves coercion for Node.js logging. + */ +class NodeDish extends Dish { + + /** + * Create a Dish + * @param {any} inputOrDish - The dish input + * @param {String|Number} - The dish type, as enum or string + */ + constructor(inputOrDish=null, type=null) { + + // Allow `fs` file input: + // Any node fs Buffers transformed to array buffer + // Use Array.from as Uint8Array doesnt pass instanceof Array test + if (Buffer.isBuffer(inputOrDish)) { + inputOrDish = Array.from(inputOrDish); + type = Dish.BYTE_ARRAY; + } + super(inputOrDish, type); + } + + /** + * Apply the inputted operation to the dish. + * + * @param {WrappedOperation} operation the operation to perform + * @param {*} args - any arguments for the operation + * @returns {Dish} a new dish with the result of the operation. + */ + apply(operation, args=null) { + return operation(this, args); + } + + /** + * alias for get + * @param args see get args + */ + to(...args) { + return this.get(...args); + } + + /** + * Avoid coercion to a String primitive. + */ + toString() { + return this.presentAs(Dish.typeEnum("string")); + } + + /** + * What we want to log to the console. + */ + [util.inspect.custom](depth, options) { + return this.presentAs(Dish.typeEnum("string")); + } + + /** + * Backwards compatibility for node v6 + * Log only the value to the console in node. + */ + inspect() { + return this.presentAs(Dish.typeEnum("string")); + } + + /** + * Avoid coercion to a Number primitive. + */ + valueOf() { + return this.presentAs(Dish.typeEnum("number")); + } + +} + +export default NodeDish; diff --git a/src/node/NodeRecipe.mjs b/src/node/NodeRecipe.mjs new file mode 100644 index 00000000..2759073e --- /dev/null +++ b/src/node/NodeRecipe.mjs @@ -0,0 +1,92 @@ +/** + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import {operations} from "./index.mjs"; +import { sanitise } from "./apiUtils.mjs"; + +/** + * Similar to core/Recipe, Recipe controls a list of operations and + * the NodeDish the operate on. However, this Recipe is for the node + * environment. + */ +class NodeRecipe { + + /** + * Recipe constructor + * @param recipeConfig + */ + constructor(recipeConfig) { + this._parseConfig(recipeConfig); + } + + + /** + * Validate an ingredient $ coerce to operation if necessary. + * @param {String | Function | Object} ing + */ + _validateIngredient(ing) { + if (typeof ing === "string") { + const op = operations.find((op) => { + return sanitise(op.opName) === sanitise(ing); + }); + if (op) { + return op; + } else { + throw new TypeError(`Couldn't find an operation with name '${ing}'.`); + } + } else if (typeof ing === "function") { + if (operations.includes(ing)) { + return ing; + } else { + throw new TypeError("Inputted function not a Chef operation."); + } + // CASE: op with configuration + } else if (ing.op && ing.args) { + // Return op and args pair for opList item. + const sanitisedOp = this._validateIngredient(ing.op); + return {op: sanitisedOp, args: ing.args}; + } else { + throw new TypeError("Recipe can only contain function names or functions"); + } + } + + + /** + * Parse config for recipe. + * @param {String | Function | String[] | Function[] | [String | Function]} recipeConfig + */ + _parseConfig(recipeConfig) { + if (!recipeConfig) { + this.opList = []; + return; + } + + if (!Array.isArray(recipeConfig)) { + recipeConfig = [recipeConfig]; + } + + this.opList = recipeConfig.map((ing) => this._validateIngredient(ing)); + } + + /** + * Run the dish through each operation, one at a time. + * @param {NodeDish} dish + * @returns {NodeDish} + */ + execute(dish) { + return this.opList.reduce((prev, curr) => { + // CASE where opList item is op and args + if (Object.prototype.hasOwnProperty.call(curr, "op") && + Object.prototype.hasOwnProperty.call(curr, "args")) { + return curr.op(prev, curr.args); + } + // CASE opList item is just op. + return curr(prev); + }, dish); + } +} + +export default NodeRecipe; diff --git a/src/node/api.mjs b/src/node/api.mjs new file mode 100644 index 00000000..e136143c --- /dev/null +++ b/src/node/api.mjs @@ -0,0 +1,333 @@ +/** + * Wrap operations for consumption in Node. + * + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +/*eslint no-console: ["off"] */ + +import NodeDish from "./NodeDish.mjs"; +import NodeRecipe from "./NodeRecipe.mjs"; +import OperationConfig from "../core/config/OperationConfig.json"; +import { sanitise, removeSubheadingsFromArray, sentenceToCamelCase } from "./apiUtils.mjs"; +import ExludedOperationError from "../core/errors/ExcludedOperationError.mjs"; + + +/** + * transformArgs + * + * Take the default args array and update with any user-defined + * operation arguments. Allows user to define arguments in object style, + * with accommodating name matching. Using named args in the API is more + * clear to the user. + * + * Argument name matching is case and space insensitive + * @private + * @param {Object[]} originalArgs - the operation-s args list + * @param {Object} newArgs - any inputted args + */ +function transformArgs(opArgsList, newArgs) { + + if (newArgs && Array.isArray(newArgs)) { + return newArgs; + } + + // Filter out arg values that are list subheadings - they are surrounded in []. + // See Strings op for example. + const opArgs = Object.assign([], opArgsList).map((a) => { + if (Array.isArray(a.value)) { + a.value = removeSubheadingsFromArray(a.value); + } + return a; + }); + + // Reconcile object style arg info to fit operation args shape. + if (newArgs) { + Object.keys(newArgs).map((key) => { + const index = opArgs.findIndex((arg) => { + return arg.name.toLowerCase().replace(/ /g, "") === + key.toLowerCase().replace(/ /g, ""); + }); + + if (index > -1) { + const argument = opArgs[index]; + if (argument.type === "toggleString") { + if (typeof newArgs[key] === "string") { + argument.string = newArgs[key]; + } else { + argument.string = newArgs[key].string; + argument.option = newArgs[key].option; + } + } else if (argument.type === "editableOption") { + // takes key: "option", key: {name, val: "string"}, key: {name, val: [...]} + argument.value = typeof newArgs[key] === "string" ? newArgs[key]: newArgs[key].value; + } else { + argument.value = newArgs[key]; + } + } + }); + } + + // Sanitise args + return opArgs.map((arg) => { + if (arg.type === "option") { + // pick default option if not already chosen + return typeof arg.value === "string" ? arg.value : arg.value[0]; + } + + if (arg.type === "editableOption") { + return typeof arg.value === "string" ? arg.value : arg.value[0].value; + } + + if (arg.type === "toggleString") { + // ensure string and option exist when user hasn't defined + arg.string = arg.string || ""; + arg.option = arg.option || arg.toggleValues[0]; + return arg; + } + + return arg.value; + }); +} + + +/** + * Ensure an input is a SyncDish object. + * @param input + */ +function ensureIsDish(input) { + if (!input) { + return new NodeDish(); + } + + if (input instanceof NodeDish) { + return input; + } else { + return new NodeDish(input); + } +} + + +/** + * prepareOp: transform args, make input the right type. + * Also convert any Buffers to ArrayBuffers. + * @param opInstance - instance of the operation + * @param input - operation input + * @param args - operation args + */ +function prepareOp(opInstance, input, args) { + const dish = ensureIsDish(input); + // Transform object-style args to original args array + const transformedArgs = transformArgs(opInstance.args, args); + const transformedInput = dish.get(opInstance.inputType); + return {transformedInput, transformedArgs}; +} + + +/** + * createArgInfo + * + * Create an object of options for each argument in the given operation + * + * Argument names are converted to camel case for consistency. + * + * @param {Operation} op - the operation to extract args from + * @returns {{}} - arrays of options for args. +*/ +function createArgInfo(op) { + const result = {}; + op.args.forEach((a) => { + if (a.type === "option" || a.type === "editableOption") { + result[sentenceToCamelCase(a.name)] = { + type: a.type, + options: removeSubheadingsFromArray(a.value) + }; + } else if (a.type === "toggleString") { + result[sentenceToCamelCase(a.name)] = { + type: a.type, + value: a.value, + toggleValues: removeSubheadingsFromArray(a.toggleValues), + }; + } else { + result[sentenceToCamelCase(a.name)] = { + type: a.type, + value: a.value, + }; + } + }); + + return result; +} + + +/** + * Wrap an operation to be consumed by node API. + * Checks to see if run function is async or not. + * new Operation().run() becomes operation() + * Perform type conversion on input + * @private + * @param {Operation} Operation + * @returns {Function} The operation's run function, wrapped in + * some type conversion logic + */ +export function _wrap(OpClass) { + + // Check to see if class's run function is async. + const opInstance = new OpClass(); + const isAsync = opInstance.run.constructor.name === "AsyncFunction"; + + let wrapped; + + // If async, wrap must be async. + if (isAsync) { + /** + * Async wrapped operation run function + * @param {*} input + * @param {Object | String[]} args - either in Object or normal args array + * @returns {Promise} operation's output, on a Dish. + * @throws {OperationError} if the operation throws one. + */ + wrapped = async (input, args=null) => { + const {transformedInput, transformedArgs} = prepareOp(opInstance, input, args); + const result = await opInstance.run(transformedInput, transformedArgs); + return new NodeDish({ + value: result, + type: opInstance.outputType, + }); + }; + } else { + /** + * wrapped operation run function + * @param {*} input + * @param {Object | String[]} args - either in Object or normal args array + * @returns {SyncDish} operation's output, on a Dish. + * @throws {OperationError} if the operation throws one. + */ + wrapped = (input, args=null) => { + const {transformedInput, transformedArgs} = prepareOp(opInstance, input, args); + const result = opInstance.run(transformedInput, transformedArgs); + return new NodeDish({ + value: result, + type: opInstance.outputType, + }); + }; + } + + // used in chef.help + wrapped.opName = OpClass.name; + wrapped.args = createArgInfo(opInstance); + + return wrapped; +} + + +/** + * help: Give information about operations matching the given search term, + * or inputted operation. + * + * @param {String || wrapped operation} input - the name of the operation to get help for. + * Case and whitespace are ignored in search. + * @returns {Object[]} Config of matching operations. + */ +export function help(input) { + let searchTerm = false; + if (typeof input === "string") { + searchTerm = input; + } else if (typeof input === "function") { + searchTerm = input.opName; + } + + if (!searchTerm) { + return null; + } + + let exactMatchExists = false; + + // Look for matches in operation name and description, listing name + // matches first. + const matches = Object.keys(OperationConfig) + // hydrate operation: swap op name for op config object (with name) + .map((m) => { + const hydrated = OperationConfig[m]; + hydrated.name = m; + + // flag up an exact name match. Only first exact match counts. + if (!exactMatchExists) { + exactMatchExists = sanitise(hydrated.name) === sanitise(searchTerm); + } + // Return hydrated along with what type of match it was + return { + hydrated, + nameExactMatch: sanitise(hydrated.name) === sanitise(searchTerm), + nameMatch: sanitise(hydrated.name).includes(sanitise(searchTerm)), + descMatch: sanitise(hydrated.description).includes(sanitise(searchTerm)) + }; + }) + // Filter out non-matches. If exact match exists, filter out all others. + .filter((result) => { + if (exactMatchExists) { + return !!result.nameExactMatch; + } + return result.nameMatch || result.descMatch; + }) + // sort results with name match first + .sort((a, b) => { + const aInt = a.nameMatch ? 1 : 0; + const bInt = b.nameMatch ? 1 : 0; + return bInt - aInt; + }) + // extract just the hydrated config + .map(result => result.hydrated); + + if (matches && matches.length) { + console.log(`${matches.length} result${matches.length > 1 ? "s" : ""} found.`); + return matches; + } + + console.log("No results found."); + return null; +} + + +/** + * bake [Wrapped] - Perform an array of operations on some input. + * @returns {Function} + */ +export function bake(){ + + /** + * bake + * + * @param {*} input - some input for a recipe. + * @param {String | Function | String[] | Function[] | [String | Function]} recipeConfig - + * An operation, operation name, or an array of either. + * @returns {SyncDish} of the result + * @throws {TypeError} if invalid recipe given. + */ + return function(input, recipeConfig) { + const recipe = new NodeRecipe(recipeConfig); + const dish = ensureIsDish(input); + return recipe.execute(dish); + }; +} + + +/** + * explainExcludedFunction + * + * Explain that the given operation is not included in the Node.js version. + * @param {String} name - name of operation + */ +export function _explainExludedFunction(name) { + /** + * Throw new error type with useful message. + */ + const func = () => { + throw new ExludedOperationError(`Sorry, the ${name} operation is not available in the Node.js version of CyberChef.`); + }; + // Add opName prop so NodeRecipe can handle it, just like wrap does. + func.opName = name; + return func; +} diff --git a/src/node/apiUtils.mjs b/src/node/apiUtils.mjs new file mode 100644 index 00000000..8d530083 --- /dev/null +++ b/src/node/apiUtils.mjs @@ -0,0 +1,86 @@ +/** + * Utility functions for the node environment + * + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + + +/** + * someName => Somename + * + * @param {String} str = string to be altered + * @returns {String} + */ +const capitalise = function capitalise(str) { + // Don't edit names that start with 2+ caps + if (/^[A-Z0-9]{2,}/g.test(str)) { + return str; + } + // reserved. Don't change for now. + if (str === "Return") { + return str; + } + + return `${str.charAt(0).toUpperCase()}${str.substr(1).toLowerCase()}`; +}; + + +/** + * SomeName => someName + * @param {String} name - string to be altered + * @returns {String} decapitalised + */ +export function decapitalise(str) { + // Don't decapitalise str that start with 2+ caps + if (/^[A-Z0-9]{2,}/g.test(str)) { + return str; + } + // reserved. Don't change for now. + if (str === "Return") { + return str; + } + + return `${str.charAt(0).toLowerCase()}${str.substr(1)}`; +} + + +/** + * Remove strings surrounded with [] from the given array. +*/ +export function removeSubheadingsFromArray(array) { + if (Array.isArray(array)) { + return array.filter((i) => { + if (typeof i === "string") { + return !i.match(/^\[[\s\S]*\]$/); + } + return true; + }); + } +} + + +/** + * Remove spaces, make lower case. + * @param str + */ +export function sanitise(str) { + return str.replace(/ /g, "").toLowerCase(); +} + + +/** + * sonething like this => somethingLikeThis + * ABC a sentence => ABCASentence +*/ +export function sentenceToCamelCase(str) { + return str.split(" ") + .map((s, index) => { + if (index === 0) { + return decapitalise(s); + } + return capitalise(s); + }) + .reduce((prev, curr) => `${prev}${curr}`, ""); +} diff --git a/src/node/cjs.js b/src/node/cjs.js new file mode 100644 index 00000000..a17bce53 --- /dev/null +++ b/src/node/cjs.js @@ -0,0 +1,13 @@ +/** + * Export the main ESM module as CommonJS + * + * + * @author d98762656 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +/*eslint no-global-assign: ["off"] */ +require = require("esm")(module); +module.exports = require("./index.mjs"); +module.exports.File = require("./File.mjs"); diff --git a/src/node/config/excludedOperations.mjs b/src/node/config/excludedOperations.mjs new file mode 100644 index 00000000..f9873eaf --- /dev/null +++ b/src/node/config/excludedOperations.mjs @@ -0,0 +1,24 @@ +/** + * Operations to exlude from the Node API + * + * @author d98762656 [d98762625@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +export default [ + // This functionality can be done more easily using JavaScript + "Fork", + "Merge", + "Jump", + "ConditionalJump", + "Label", + "Comment", + + // esprima doesn't work in .mjs + "JavaScriptBeautify", + "JavaScriptMinify", + "JavaScriptParser", + + // Irrelevant in Node console + "SyntaxHighlighter", +]; diff --git a/src/node/config/scripts/generateNodeIndex.mjs b/src/node/config/scripts/generateNodeIndex.mjs new file mode 100644 index 00000000..d6fe9295 --- /dev/null +++ b/src/node/config/scripts/generateNodeIndex.mjs @@ -0,0 +1,124 @@ +/** + * This script generates the exports functionality for the node API. + * + * it exports chef as default, but all the wrapped operations as + * other top level exports. + * + * @author d98762656 [d98762625@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +/*eslint no-console: 0 */ + +import fs from "fs"; +import path from "path"; +import * as operations from "../../../core/operations/index.mjs"; +import { decapitalise } from "../../apiUtils.mjs"; +import excludedOperations from "../excludedOperations.mjs"; + +const includedOperations = Object.keys(operations).filter((op => excludedOperations.indexOf(op) === -1)); + +const dir = path.join(`${process.cwd()}/src/node`); +if (!fs.existsSync(dir)) { + console.log("\nCWD: " + process.cwd()); + console.log("Error: generateNodeIndex.mjs should be run from the project root"); + console.log("Example> node --experimental-modules src/core/config/scripts/generateNodeIndex.mjs"); + process.exit(1); +} + +let code = `/** +* THIS FILE IS AUTOMATICALLY GENERATED BY src/node/config/scripts/generateNodeIndex.mjs +* +* @author d98762625 [d98762625@gmail.com] +* @copyright Crown Copyright 2019 +* @license Apache-2.0 +*/ + +/* eslint camelcase: 0 */ + + +import NodeDish from "./NodeDish.mjs"; +import { _wrap, help, bake, _explainExludedFunction } from "./api.mjs"; +import File from "./File.mjs"; +import { + // import as core_ to avoid name clashes after wrap. +`; + +includedOperations.forEach((op) => { + // prepend with core_ to avoid name collision later. + code += ` ${op} as core_${op},\n`; +}); + +code +=` +} from "../core/operations/index"; + +global.File = File; + +/** + * generateChef + * + * Creates decapitalised, wrapped ops in chef object for default export. + */ +function generateChef() { + return { +`; + +includedOperations.forEach((op) => { + code += ` "${decapitalise(op)}": _wrap(core_${op}),\n`; +}); + +excludedOperations.forEach((op) => { + code += ` "${decapitalise(op)}": _explainExludedFunction("${op}"),\n`; +}); + +code += ` }; +} + +const chef = generateChef(); +// Add some additional features to chef object. +chef.help = help; +chef.Dish = NodeDish; + +// Define consts here so we can add to top-level export - wont allow +// export of chef property. +`; + +Object.keys(operations).forEach((op) => { + code += `const ${decapitalise(op)} = chef.${decapitalise(op)};\n`; +}); + +code +=` + +// Define array of all operations to create register for bake. +const operations = [\n`; + +Object.keys(operations).forEach((op) => { + code += ` ${decapitalise(op)},\n`; +}); + +code += `]; + +const prebaked = bake(operations); +chef.bake = prebaked; +export default chef; + +// Operations as top level exports. +export { + operations, +`; + +Object.keys(operations).forEach((op) => { + code += ` ${decapitalise(op)},\n`; +}); + +code += " NodeDish as Dish,\n"; +code += " prebaked as bake,\n"; +code += " help,\n"; +code += "};\n"; + + +fs.writeFileSync( + path.join(dir, "./index.mjs"), + code +); diff --git a/src/node/index.mjs b/src/node/index.mjs deleted file mode 100644 index 4a68e12a..00000000 --- a/src/node/index.mjs +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Node view for CyberChef. - * - * @author n1474335 [n1474335@gmail.com] - * @copyright Crown Copyright 2017 - * @license Apache-2.0 - */ - -// Define global environment functions -global.ENVIRONMENT_IS_WORKER = function() { - return typeof importScripts === "function"; -}; -global.ENVIRONMENT_IS_NODE = function() { - return typeof process === "object" && typeof require === "function"; -}; -global.ENVIRONMENT_IS_WEB = function() { - return typeof window === "object"; -}; - -import Chef from "../core/Chef"; - -const CyberChef = { - - bake: function(input, recipeConfig) { - this.chef = new Chef(); - return this.chef.bake( - input, - recipeConfig, - {}, - 0, - false - ); - } - -}; - -export default CyberChef; -export {CyberChef}; diff --git a/src/node/repl.js b/src/node/repl.js new file mode 100644 index 00000000..c9ed76f1 --- /dev/null +++ b/src/node/repl.js @@ -0,0 +1,36 @@ +/** + * Create a REPL server for chef + * + * + * @author d98762656 [d98762625@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +const chef = require("./cjs.js"); +const repl = require("repl"); + + +/*eslint no-console: ["off"] */ + +console.log(` + ______ __ ________ ____ + / ____/_ __/ /_ ___ _____/ ____/ /_ ___ / __/ + / / / / / / __ \\/ _ \\/ ___/ / / __ \\/ _ \\/ /_ +/ /___/ /_/ / /_/ / __/ / / /___/ / / / __/ __/ +\\____/\\__, /_.___/\\___/_/ \\____/_/ /_/\\___/_/ + /____/ + +`); +const replServer = repl.start({ + prompt: "chef > ", +}); + +global.File = chef.File; + +Object.keys(chef).forEach((key) => { + if (key !== "operations") { + replServer.context[key] = chef[key]; + } +}); + diff --git a/src/web/App.mjs b/src/web/App.mjs index ac97de4c..56746a41 100755 --- a/src/web/App.mjs +++ b/src/web/App.mjs @@ -41,6 +41,7 @@ class App { this.autoBakePause = false; this.progress = 0; this.ingId = 0; + this.timeouts = {}; } @@ -87,7 +88,10 @@ class App { setTimeout(function() { document.getElementById("loader-wrapper").remove(); document.body.classList.remove("loaded"); - }, 1000); + + // Bake initial input + this.manager.input.bakeAll(); + }.bind(this), 1000); // Clear the loading message interval clearInterval(window.loadingMsgsInt); @@ -96,6 +100,9 @@ class App { window.removeEventListener("error", window.loadingErrorHandler); document.dispatchEvent(this.manager.apploaded); + + this.manager.input.calcMaxTabs(); + this.manager.output.calcMaxTabs(); } @@ -108,7 +115,7 @@ class App { handleError(err, logToConsole) { if (logToConsole) log.error(err); const msg = err.displayStr || err.toString(); - this.alert(msg, this.options.errorTimeout, !this.options.showErrors); + this.alert(Utils.escapeHtml(msg), this.options.errorTimeout, !this.options.showErrors); } @@ -128,7 +135,6 @@ class App { this.manager.recipe.updateBreakpointIndicator(false); this.manager.worker.bake( - this.getInput(), // The user's input this.getRecipeConfig(), // The configuration of the recipe this.options, // Options set by the user this.progress, // The current position in the recipe @@ -148,13 +154,46 @@ class App { if (this.autoBake_ && !this.baking) { log.debug("Auto-baking"); - this.bake(); + this.manager.input.inputWorker.postMessage({ + action: "autobake", + data: { + activeTab: this.manager.tabs.getActiveInputTab() + } + }); } else { this.manager.controls.showStaleIndicator(); } } + /** + * Executes the next step of the recipe. + */ + step() { + if (this.baking) return; + + // Reset status using cancelBake + this.manager.worker.cancelBake(true, false); + + const activeTab = this.manager.tabs.getActiveInputTab(); + if (activeTab === -1) return; + + let progress = 0; + if (this.manager.output.outputs[activeTab].progress !== false) { + log.error(this.manager.output.outputs[activeTab]); + progress = this.manager.output.outputs[activeTab].progress; + } + + this.manager.input.inputWorker.postMessage({ + action: "step", + data: { + activeTab: activeTab, + progress: progress + 1 + } + }); + } + + /** * Runs a silent bake, forcing the browser to load and cache all the relevant JavaScript code needed * to do a real bake. @@ -175,24 +214,25 @@ class App { } - /** - * Gets the user's input data. - * - * @returns {string} - */ - getInput() { - return this.manager.input.get(); - } - - /** * Sets the user's input data. * * @param {string} input - The string to set the input to - * @param {boolean} [silent=false] - Suppress statechange event */ - setInput(input, silent=false) { - this.manager.input.set(input, silent); + setInput(input) { + // Get the currently active tab. + // If there isn't one, assume there are no inputs so use inputNum of 1 + let inputNum = this.manager.tabs.getActiveInputTab(); + if (inputNum === -1) inputNum = 1; + this.manager.input.updateInputValue(inputNum, input); + + this.manager.input.inputWorker.postMessage({ + action: "setInput", + data: { + inputNum: inputNum, + silent: true + } + }); } @@ -216,7 +256,7 @@ class App { for (let j = 0; j < catConf.ops.length; j++) { const opName = catConf.ops[j]; - if (!this.operations.hasOwnProperty(opName)) { + if (!(opName in this.operations)) { log.warn(`${opName} could not be found.`); continue; } @@ -244,7 +284,7 @@ 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 + * @param {boolean} [minimise=false] - Set this flag if attempting to minimise frames to 0 width */ initialiseSplitter(minimise=false) { if (this.columnSplitter) this.columnSplitter.destroy(); @@ -252,12 +292,14 @@ class App { this.columnSplitter = Split(["#operations", "#recipe", "#IO"], { sizes: [20, 30, 50], - minSize: minimise ? [0, 0, 0] : [240, 370, 450], + minSize: minimise ? [0, 0, 0] : [240, 310, 450], gutterSize: 4, - expandToMin: false, - onDrag: function() { + expandToMin: true, + onDrag: this.debounce(function() { this.manager.recipe.adjustWidth(); - }.bind(this) + this.manager.input.calcMaxTabs(); + this.manager.output.calcMaxTabs(); + }, 50, "dragSplitter", this, []) }); this.ioSplitter = Split(["#input", "#output"], { @@ -330,7 +372,7 @@ class App { validFavourites(favourites) { const validFavs = []; for (let i = 0; i < favourites.length; i++) { - if (this.operations.hasOwnProperty(favourites[i])) { + if (favourites[i] in this.operations) { validFavs.push(favourites[i]); } else { this.alert(`The operation "${Utils.escapeHtml(favourites[i])}" is no longer available. ` + @@ -391,11 +433,12 @@ class App { this.manager.recipe.initialiseOperationDragNDrop(); } - /** - * Checks for input and recipe in the URI parameters and loads them if present. + * Gets the URI params from the window and parses them to extract the actual values. + * + * @returns {object} */ - loadURIParams() { + getURIParams() { // Load query string or hash from URI (depending on which is populated) // We prefer getting the hash by splitting the href rather than referencing // location.hash as some browsers (Firefox) automatically URL decode it, @@ -403,8 +446,20 @@ class App { const params = window.location.search || window.location.href.split("#")[1] || window.location.hash; - this.uriParams = Utils.parseURIParams(params); + const parsedParams = Utils.parseURIParams(params); + return parsedParams; + } + + /** + * Searches the URI parameters for recipe and input parameters. + * If recipe is present, replaces the current recipe with the recipe provided in the URI. + * If input is present, decodes and sets the input to the one provided in the URI. + * + * @fires Manager#statechange + */ + loadURIParams() { this.autoBakePause = true; + this.uriParams = this.getURIParams(); // Read in recipe from URI params if (this.uriParams.recipe) { @@ -433,7 +488,7 @@ class App { if (this.uriParams.input) { try { const inputData = fromBase64(this.uriParams.input); - this.setInput(inputData, true); + this.setInput(inputData); } catch (err) {} } @@ -522,6 +577,8 @@ class App { this.columnSplitter.setSizes([20, 30, 50]); this.ioSplitter.setSizes([50, 50]); this.manager.recipe.adjustWidth(); + this.manager.input.calcMaxTabs(); + this.manager.output.calcMaxTabs(); } @@ -656,6 +713,17 @@ class App { this.progress = 0; this.autoBake(); + this.updateTitle(false, null, true); + } + + /** + * Update the page title to contain the new recipe + * + * @param {boolean} includeInput + * @param {string} input + * @param {boolean} [changeUrl=true] + */ + updateTitle(includeInput, input, changeUrl=true) { // Set title const recipeConfig = this.getRecipeConfig(); let title = "CyberChef"; @@ -674,8 +742,8 @@ class App { document.title = title; // Update the current history state (not creating a new one) - if (this.options.updateUrl) { - this.lastStateUrl = this.manager.controls.generateStateUrl(true, true, recipeConfig); + if (this.options.updateUrl && changeUrl) { + this.lastStateUrl = this.manager.controls.generateStateUrl(true, includeInput, input, recipeConfig); window.history.replaceState({}, title, this.lastStateUrl); } } @@ -691,6 +759,29 @@ class App { this.loadURIParams(); } + + /** + * Debouncer to stop functions from being executed multiple times in a + * short space of time + * https://davidwalsh.name/javascript-debounce-function + * + * @param {function} func - The function to be executed after the debounce time + * @param {number} wait - The time (ms) to wait before executing the function + * @param {string} id - Unique ID to reference the timeout for the function + * @param {object} scope - The object to bind to the debounced function + * @param {array} args - Array of arguments to be passed to func + * @returns {function} + */ + debounce(func, wait, id, scope, args) { + return function() { + const later = function() { + func.apply(scope, args); + }; + clearTimeout(this.timeouts[id]); + this.timeouts[id] = setTimeout(later, wait); + }.bind(this); + } + } export default App; diff --git a/src/web/HTMLIngredient.mjs b/src/web/HTMLIngredient.mjs index d8ac9511..6f3f5e66 100755 --- a/src/web/HTMLIngredient.mjs +++ b/src/web/HTMLIngredient.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import Utils from "../core/Utils"; +import Utils from "../core/Utils.mjs"; /** * Object to handle the creation of operation ingredients. @@ -293,7 +293,9 @@ class HTMLIngredient { const op = el.parentNode.parentNode; const target = op.querySelectorAll(".arg")[this.target]; - target.value = el.childNodes[el.selectedIndex].getAttribute("populate-value"); + const popVal = el.childNodes[el.selectedIndex].getAttribute("populate-value"); + if (popVal !== "") target.value = popVal; + const evt = new Event("change"); target.dispatchEvent(evt); diff --git a/src/web/HTMLOperation.mjs b/src/web/HTMLOperation.mjs index 5373113a..fe075c48 100755 --- a/src/web/HTMLOperation.mjs +++ b/src/web/HTMLOperation.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import HTMLIngredient from "./HTMLIngredient"; +import HTMLIngredient from "./HTMLIngredient.mjs"; /** diff --git a/src/web/InputWaiter.mjs b/src/web/InputWaiter.mjs deleted file mode 100755 index 17b48b6f..00000000 --- a/src/web/InputWaiter.mjs +++ /dev/null @@ -1,354 +0,0 @@ -/** - * @author n1474335 [n1474335@gmail.com] - * @copyright Crown Copyright 2016 - * @license Apache-2.0 - */ - -import LoaderWorker from "worker-loader?inline&fallback=false!./LoaderWorker"; -import Utils from "../core/Utils"; - - -/** - * Waiter to handle events related to the input. - */ -class InputWaiter { - - /** - * InputWaiter constructor. - * - * @param {App} app - The main view object for CyberChef. - * @param {Manager} manager - The CyberChef event manager. - */ - constructor(app, manager) { - this.app = app; - this.manager = manager; - - // Define keys that don't change the input so we don't have to autobake when they are pressed - this.badKeys = [ - 16, //Shift - 17, //Ctrl - 18, //Alt - 19, //Pause - 20, //Caps - 27, //Esc - 33, 34, 35, 36, //PgUp, PgDn, End, Home - 37, 38, 39, 40, //Directional - 44, //PrntScrn - 91, 92, //Win - 93, //Context - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, //F1-12 - 144, //Num - 145, //Scroll - ]; - - this.loaderWorker = null; - this.fileBuffer = null; - } - - - /** - * Gets the user's input from the input textarea. - * - * @returns {string} - */ - get() { - return this.fileBuffer || document.getElementById("input-text").value; - } - - - /** - * Sets the input in the input area. - * - * @param {string|File} input - * @param {boolean} [silent=false] - Suppress statechange event - * - * @fires Manager#statechange - */ - set(input, silent=false) { - const inputText = document.getElementById("input-text"); - if (input instanceof File) { - this.setFile(input); - inputText.value = ""; - this.setInputInfo(input.size, null); - } else { - inputText.value = input; - this.closeFile(); - if (!silent) window.dispatchEvent(this.manager.statechange); - const lines = input.length < (this.app.options.ioDisplayThreshold * 1024) ? - input.count("\n") + 1 : null; - this.setInputInfo(input.length, lines); - } - } - - - /** - * Shows file details. - * - * @param {File} file - */ - setFile(file) { - // Display file overlay in input area with details - const fileOverlay = document.getElementById("input-file"), - fileName = document.getElementById("input-file-name"), - fileSize = document.getElementById("input-file-size"), - fileType = document.getElementById("input-file-type"), - fileLoaded = document.getElementById("input-file-loaded"); - - this.fileBuffer = new ArrayBuffer(); - fileOverlay.style.display = "block"; - fileName.textContent = file.name; - fileSize.textContent = file.size.toLocaleString() + " bytes"; - fileType.textContent = file.type || "unknown"; - fileLoaded.textContent = "0%"; - } - - - /** - * Displays information about the input. - * - * @param {number} length - The length of the current input string - * @param {number} lines - The number of the lines in the current input string - */ - setInputInfo(length, lines) { - let width = length.toString().length; - width = width < 2 ? 2 : width; - - const lengthStr = length.toString().padStart(width, " ").replace(/ /g, " "); - let msg = "length: " + lengthStr; - - if (typeof lines === "number") { - const linesStr = lines.toString().padStart(width, " ").replace(/ /g, " "); - msg += "
lines: " + linesStr; - } - - document.getElementById("input-info").innerHTML = msg; - } - - - /** - * Handler for input change events. - * - * @param {event} e - * - * @fires Manager#statechange - */ - inputChange(e) { - // Ignore this function if the input is a File - if (this.fileBuffer) return; - - // Remove highlighting from input and output panes as the offsets might be different now - this.manager.highlighter.removeHighlights(); - - // Reset recipe progress as any previous processing will be redundant now - this.app.progress = 0; - - // Update the input metadata info - const inputText = this.get(); - const lines = inputText.length < (this.app.options.ioDisplayThreshold * 1024) ? - inputText.count("\n") + 1 : null; - - this.setInputInfo(inputText.length, lines); - - if (e && this.badKeys.indexOf(e.keyCode) < 0) { - // Fire the statechange event as the input has been modified - window.dispatchEvent(this.manager.statechange); - } - } - - - /** - * Handler for input paste events. - * Checks that the size of the input is below the display limit, otherwise treats it as a file/blob. - * - * @param {event} e - */ - inputPaste(e) { - const pastedData = e.clipboardData.getData("Text"); - - if (pastedData.length < (this.app.options.ioDisplayThreshold * 1024)) { - this.inputChange(e); - } else { - e.preventDefault(); - e.stopPropagation(); - - const file = new File([pastedData], "PastedData", { - type: "text/plain", - lastModified: Date.now() - }); - - this.loaderWorker = new LoaderWorker(); - this.loaderWorker.addEventListener("message", this.handleLoaderMessage.bind(this)); - this.loaderWorker.postMessage({"file": file}); - this.set(file); - return false; - } - } - - - /** - * Handler for input dragover events. - * Gives the user a visual cue to show that items can be dropped here. - * - * @param {event} e - */ - inputDragover(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("#input-text,#input-file").classList.add("dropping-file"); - } - - - /** - * Handler for input dragleave events. - * Removes the visual cue. - * - * @param {event} e - */ - inputDragleave(e) { - e.stopPropagation(); - e.preventDefault(); - document.getElementById("input-text").classList.remove("dropping-file"); - document.getElementById("input-file").classList.remove("dropping-file"); - } - - - /** - * Handler for input drop events. - * Loads the dragged data into the input textarea. - * - * @param {event} e - */ - inputDrop(e) { - // This will be set if we're dragging an operation - if (e.dataTransfer.effectAllowed === "move") - return false; - - e.stopPropagation(); - e.preventDefault(); - - const file = e.dataTransfer.files[0]; - const text = e.dataTransfer.getData("Text"); - - document.getElementById("input-text").classList.remove("dropping-file"); - document.getElementById("input-file").classList.remove("dropping-file"); - - if (text) { - this.closeFile(); - this.set(text); - return; - } - - if (file) { - this.loadFile(file); - } - } - - /** - * Handler for open input button events - * Loads the opened data into the input textarea - * - * @param {event} e - */ - inputOpen(e) { - e.preventDefault(); - const file = e.srcElement.files[0]; - this.loadFile(file); - } - - - /** - * Handler for messages sent back by the LoaderWorker. - * - * @param {MessageEvent} e - */ - handleLoaderMessage(e) { - const r = e.data; - if (r.hasOwnProperty("progress")) { - const fileLoaded = document.getElementById("input-file-loaded"); - fileLoaded.textContent = r.progress + "%"; - } - - if (r.hasOwnProperty("error")) { - this.app.alert(r.error, 10000); - } - - if (r.hasOwnProperty("fileBuffer")) { - log.debug("Input file loaded"); - this.fileBuffer = r.fileBuffer; - this.displayFilePreview(); - window.dispatchEvent(this.manager.statechange); - } - } - - - /** - * Shows a chunk of the file in the input behind the file overlay. - */ - displayFilePreview() { - const inputText = document.getElementById("input-text"), - fileSlice = this.fileBuffer.slice(0, 4096); - - inputText.style.overflow = "hidden"; - inputText.classList.add("blur"); - inputText.value = Utils.printable(Utils.arrayBufferToStr(fileSlice)); - if (this.fileBuffer.byteLength > 4096) { - inputText.value += "[truncated]..."; - } - } - - - /** - * Handler for file close events. - */ - closeFile() { - if (this.loaderWorker) this.loaderWorker.terminate(); - this.fileBuffer = null; - document.getElementById("input-file").style.display = "none"; - const inputText = document.getElementById("input-text"); - inputText.style.overflow = "auto"; - inputText.classList.remove("blur"); - } - - - /** - * Loads a file into the input. - * - * @param {File} file - */ - loadFile(file) { - if (file) { - this.closeFile(); - this.loaderWorker = new LoaderWorker(); - this.loaderWorker.addEventListener("message", this.handleLoaderMessage.bind(this)); - this.loaderWorker.postMessage({"file": file}); - this.set(file); - } - } - - - /** - * Handler for clear IO events. - * Resets the input, output and info areas. - * - * @fires Manager#statechange - */ - clearIoClick() { - this.closeFile(); - this.manager.output.closeFile(); - this.manager.highlighter.removeHighlights(); - document.getElementById("input-text").value = ""; - document.getElementById("output-text").value = ""; - document.getElementById("input-info").innerHTML = ""; - document.getElementById("output-info").innerHTML = ""; - document.getElementById("input-selection-info").innerHTML = ""; - document.getElementById("output-selection-info").innerHTML = ""; - window.dispatchEvent(this.manager.statechange); - } - -} - -export default InputWaiter; diff --git a/src/web/LoaderWorker.js b/src/web/LoaderWorker.js deleted file mode 100755 index 076e0e7d..00000000 --- a/src/web/LoaderWorker.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Web Worker to load large amounts of data without locking up the UI. - * - * @author n1474335 [n1474335@gmail.com] - * @copyright Crown Copyright 2017 - * @license Apache-2.0 - */ - - -/** - * Respond to message from parent thread. - */ -self.addEventListener("message", function(e) { - const r = e.data; - if (r.hasOwnProperty("file")) { - self.loadFile(r.file); - } -}); - - -/** - * Loads a file object into an ArrayBuffer, then transfers it back to the parent thread. - * - * @param {File} file - */ -self.loadFile = function(file) { - const reader = new FileReader(); - const data = new Uint8Array(file.size); - let offset = 0; - const CHUNK_SIZE = 10485760; // 10MiB - - const seek = function() { - if (offset >= file.size) { - self.postMessage({"progress": 100}); - self.postMessage({"fileBuffer": data.buffer}, [data.buffer]); - return; - } - self.postMessage({"progress": Math.round(offset / file.size * 100)}); - const slice = file.slice(offset, offset + CHUNK_SIZE); - reader.readAsArrayBuffer(slice); - }; - - reader.onload = function(e) { - data.set(new Uint8Array(reader.result), offset); - offset += CHUNK_SIZE; - seek(); - }; - - reader.onerror = function(e) { - self.postMessage({"error": reader.error.message}); - }; - - seek(); -}; diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index 5fa0e8c1..5ab44b21 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -4,18 +4,19 @@ * @license Apache-2.0 */ -import WorkerWaiter from "./WorkerWaiter"; -import WindowWaiter from "./WindowWaiter"; -import ControlsWaiter from "./ControlsWaiter"; -import RecipeWaiter from "./RecipeWaiter"; -import OperationsWaiter from "./OperationsWaiter"; -import InputWaiter from "./InputWaiter"; -import OutputWaiter from "./OutputWaiter"; -import OptionsWaiter from "./OptionsWaiter"; -import HighlighterWaiter from "./HighlighterWaiter"; -import SeasonalWaiter from "./SeasonalWaiter"; -import BindingsWaiter from "./BindingsWaiter"; -import BackgroundWorkerWaiter from "./BackgroundWorkerWaiter"; +import WorkerWaiter from "./waiters/WorkerWaiter.mjs"; +import WindowWaiter from "./waiters/WindowWaiter.mjs"; +import ControlsWaiter from "./waiters/ControlsWaiter.mjs"; +import RecipeWaiter from "./waiters/RecipeWaiter.mjs"; +import OperationsWaiter from "./waiters/OperationsWaiter.mjs"; +import InputWaiter from "./waiters/InputWaiter.mjs"; +import OutputWaiter from "./waiters/OutputWaiter.mjs"; +import OptionsWaiter from "./waiters/OptionsWaiter.mjs"; +import HighlighterWaiter from "./waiters/HighlighterWaiter.mjs"; +import SeasonalWaiter from "./waiters/SeasonalWaiter.mjs"; +import BindingsWaiter from "./waiters/BindingsWaiter.mjs"; +import BackgroundWorkerWaiter from "./waiters/BackgroundWorkerWaiter.mjs"; +import TabWaiter from "./waiters/TabWaiter.mjs"; /** @@ -63,6 +64,7 @@ class Manager { this.controls = new ControlsWaiter(this.app, this); this.recipe = new RecipeWaiter(this.app, this); this.ops = new OperationsWaiter(this.app, this); + this.tabs = new TabWaiter(this.app, this); this.input = new InputWaiter(this.app, this); this.output = new OutputWaiter(this.app, this); this.options = new OptionsWaiter(this.app, this); @@ -82,7 +84,9 @@ class Manager { * Sets up the various components and listeners. */ setup() { - this.worker.registerChefWorker(); + this.input.setupInputWorker(); + this.input.addInput(true); + this.worker.setupChefWorker(); this.recipe.initialiseOperationDragNDrop(); this.controls.initComponents(); this.controls.autoBakeChange(); @@ -142,11 +146,11 @@ class Manager { this.addDynamicListener("textarea.arg", "drop", this.recipe.textArgDrop, this.recipe); // Input - this.addMultiEventListener("#input-text", "keyup", this.input.inputChange, this.input); + this.addMultiEventListener("#input-text", "keyup", this.input.debounceInputChange, this.input); this.addMultiEventListener("#input-text", "paste", this.input.inputPaste, this.input); document.getElementById("reset-layout").addEventListener("click", this.app.resetLayout.bind(this.app)); - document.getElementById("clr-io").addEventListener("click", this.input.clearIoClick.bind(this.input)); - this.addListeners("#open-file", "change", this.input.inputOpen, this.input); + this.addListeners("#clr-io,#btn-close-all-tabs", "click", this.input.clearAllIoClick, this.input); + this.addListeners("#open-file,#open-folder", "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); @@ -155,9 +159,31 @@ class Manager { document.getElementById("input-text").addEventListener("mousemove", this.highlighter.inputMousemove.bind(this.highlighter)); this.addMultiEventListener("#input-text", "mousedown dblclick select", this.highlighter.inputMousedown, this.highlighter); document.querySelector("#input-file .close").addEventListener("click", this.input.clearIoClick.bind(this.input)); + document.getElementById("btn-new-tab").addEventListener("click", this.input.addInputClick.bind(this.input)); + document.getElementById("btn-previous-input-tab").addEventListener("mousedown", this.input.previousTabClick.bind(this.input)); + document.getElementById("btn-next-input-tab").addEventListener("mousedown", this.input.nextTabClick.bind(this.input)); + this.addListeners("#btn-next-input-tab,#btn-previous-input-tab", "mouseup", this.input.tabMouseUp, this.input); + this.addListeners("#btn-next-input-tab,#btn-previous-input-tab", "mouseout", this.input.tabMouseUp, this.input); + document.getElementById("btn-go-to-input-tab").addEventListener("click", this.input.goToTab.bind(this.input)); + document.getElementById("btn-find-input-tab").addEventListener("click", this.input.findTab.bind(this.input)); + this.addDynamicListener("#input-tabs li .input-tab-content", "click", this.input.changeTabClick, this.input); + document.getElementById("input-show-pending").addEventListener("change", this.input.filterTabSearch.bind(this.input)); + document.getElementById("input-show-loading").addEventListener("change", this.input.filterTabSearch.bind(this.input)); + document.getElementById("input-show-loaded").addEventListener("change", this.input.filterTabSearch.bind(this.input)); + this.addListeners("#input-filter-content,#input-filter-filename", "click", this.input.filterOptionClick, this.input); + document.getElementById("input-filter").addEventListener("change", this.input.filterTabSearch.bind(this.input)); + document.getElementById("input-filter").addEventListener("keyup", this.input.filterTabSearch.bind(this.input)); + document.getElementById("input-num-results").addEventListener("change", this.input.filterTabSearch.bind(this.input)); + document.getElementById("input-num-results").addEventListener("keyup", this.input.filterTabSearch.bind(this.input)); + document.getElementById("input-filter-refresh").addEventListener("click", this.input.filterTabSearch.bind(this.input)); + this.addDynamicListener(".input-filter-result", "click", this.input.filterItemClick, this.input); + document.getElementById("btn-open-file").addEventListener("click", this.input.inputOpenClick.bind(this.input)); + document.getElementById("btn-open-folder").addEventListener("click", this.input.folderOpenClick.bind(this.input)); + // Output document.getElementById("save-to-file").addEventListener("click", this.output.saveClick.bind(this.output)); + document.getElementById("save-all-to-file").addEventListener("click", this.output.saveAllClick.bind(this.output)); document.getElementById("copy-output").addEventListener("click", this.output.copyClick.bind(this.output)); document.getElementById("switch").addEventListener("click", this.output.switchClick.bind(this.output)); document.getElementById("undo-switch").addEventListener("click", this.output.undoSwitchClick.bind(this.output)); @@ -174,6 +200,25 @@ class Manager { this.addDynamicListener("#output-file-slice i", "click", this.output.displayFileSlice, this.output); document.getElementById("show-file-overlay").addEventListener("click", this.output.showFileOverlayClick.bind(this.output)); this.addDynamicListener(".extract-file,.extract-file i", "click", this.output.extractFileClick, this.output); + this.addDynamicListener("#output-tabs-wrapper #output-tabs li .output-tab-content", "click", this.output.changeTabClick, this.output); + document.getElementById("btn-previous-output-tab").addEventListener("mousedown", this.output.previousTabClick.bind(this.output)); + document.getElementById("btn-next-output-tab").addEventListener("mousedown", this.output.nextTabClick.bind(this.output)); + this.addListeners("#btn-next-output-tab,#btn-previous-output-tab", "mouseup", this.output.tabMouseUp, this.output); + this.addListeners("#btn-next-output-tab,#btn-previous-output-tab", "mouseout", this.output.tabMouseUp, this.output); + document.getElementById("btn-go-to-output-tab").addEventListener("click", this.output.goToTab.bind(this.output)); + document.getElementById("btn-find-output-tab").addEventListener("click", this.output.findTab.bind(this.output)); + document.getElementById("output-show-pending").addEventListener("change", this.output.filterTabSearch.bind(this.output)); + document.getElementById("output-show-baking").addEventListener("change", this.output.filterTabSearch.bind(this.output)); + document.getElementById("output-show-baked").addEventListener("change", this.output.filterTabSearch.bind(this.output)); + document.getElementById("output-show-stale").addEventListener("change", this.output.filterTabSearch.bind(this.output)); + document.getElementById("output-show-errored").addEventListener("change", this.output.filterTabSearch.bind(this.output)); + document.getElementById("output-content-filter").addEventListener("change", this.output.filterTabSearch.bind(this.output)); + document.getElementById("output-content-filter").addEventListener("keyup", this.output.filterTabSearch.bind(this.output)); + document.getElementById("output-num-results").addEventListener("change", this.output.filterTabSearch.bind(this.output)); + document.getElementById("output-num-results").addEventListener("keyup", this.output.filterTabSearch.bind(this.output)); + document.getElementById("output-filter-refresh").addEventListener("click", this.output.filterTabSearch.bind(this.output)); + this.addDynamicListener(".output-filter-result", "click", this.output.filterItemClick, this.output); + // Options document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.options)); @@ -186,6 +231,7 @@ class Manager { this.addDynamicListener(".option-item select", "change", this.options.selectChange, this.options); document.getElementById("theme").addEventListener("change", this.options.themeChange.bind(this.options)); document.getElementById("logLevel").addEventListener("change", this.options.logLevelChange.bind(this.options)); + document.getElementById("imagePreview").addEventListener("change", this.input.renderFileThumb.bind(this.input)); // Misc window.addEventListener("keydown", this.bindings.parseInput.bind(this.bindings)); @@ -275,7 +321,7 @@ class Manager { callback: callback.bind(scope || this) }; - if (this.dynamicHandlers.hasOwnProperty(eventType)) { + if (Object.prototype.hasOwnProperty.call(this.dynamicHandlers, eventType)) { // Listener already exists, add new handler to the appropriate list this.dynamicHandlers[eventType].push(eventConfig); } else { @@ -307,7 +353,6 @@ class Manager { } } } - } export default Manager; diff --git a/src/web/OutputWaiter.mjs b/src/web/OutputWaiter.mjs deleted file mode 100755 index eaafeb8b..00000000 --- a/src/web/OutputWaiter.mjs +++ /dev/null @@ -1,547 +0,0 @@ -/** - * @author n1474335 [n1474335@gmail.com] - * @copyright Crown Copyright 2016 - * @license Apache-2.0 - */ - -import Utils from "../core/Utils"; -import FileSaver from "file-saver"; - - -/** - * Waiter to handle events related to the output. - */ -class OutputWaiter { - - /** - * OutputWaiter constructor. - * - * @param {App} app - The main view object for CyberChef. - * @param {Manager} manager - The CyberChef event manager. - */ - constructor(app, manager) { - this.app = app; - this.manager = manager; - - this.dishBuffer = null; - this.dishStr = null; - } - - - /** - * Gets the output string from the output textarea. - * - * @returns {string} - */ - get() { - return document.getElementById("output-text").value; - } - - - /** - * Sets the output in the output textarea. - * - * @param {string|ArrayBuffer} data - The output string/HTML/ArrayBuffer - * @param {string} type - The data type of the output - * @param {number} duration - The length of time (ms) it took to generate the output - * @param {boolean} [preserveBuffer=false] - Whether to preserve the dishBuffer - */ - async set(data, type, duration, preserveBuffer) { - log.debug("Output type: " + type); - const outputText = document.getElementById("output-text"); - const outputHtml = document.getElementById("output-html"); - const outputFile = document.getElementById("output-file"); - const outputHighlighter = document.getElementById("output-highlighter"); - const inputHighlighter = document.getElementById("input-highlighter"); - let scriptElements, lines, length; - - if (!preserveBuffer) { - this.closeFile(); - this.dishStr = null; - document.getElementById("show-file-overlay").style.display = "none"; - } - - switch (type) { - case "html": - outputText.style.display = "none"; - outputHtml.style.display = "block"; - outputFile.style.display = "none"; - outputHighlighter.display = "none"; - inputHighlighter.display = "none"; - - outputText.value = ""; - outputHtml.innerHTML = data; - - // Execute script sections - scriptElements = outputHtml.querySelectorAll("script"); - for (let i = 0; i < scriptElements.length; i++) { - try { - eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval - } catch (err) { - log.error(err); - } - } - - await this.getDishStr(); - length = this.dishStr.length; - lines = this.dishStr.count("\n") + 1; - break; - case "ArrayBuffer": - outputText.style.display = "block"; - outputHtml.style.display = "none"; - outputHighlighter.display = "none"; - inputHighlighter.display = "none"; - - outputText.value = ""; - outputHtml.innerHTML = ""; - length = data.byteLength; - - this.setFile(data); - break; - case "string": - default: - outputText.style.display = "block"; - outputHtml.style.display = "none"; - outputFile.style.display = "none"; - outputHighlighter.display = "block"; - inputHighlighter.display = "block"; - - outputText.value = Utils.printable(data, true); - outputHtml.innerHTML = ""; - - lines = data.count("\n") + 1; - length = data.length; - this.dishStr = data; - break; - } - - this.manager.highlighter.removeHighlights(); - this.setOutputInfo(length, lines, duration); - this.backgroundMagic(); - } - - - /** - * Shows file details. - * - * @param {ArrayBuffer} buf - */ - setFile(buf) { - this.dishBuffer = buf; - const file = new File([buf], "output.dat"); - - // Display file overlay in output area with details - const fileOverlay = document.getElementById("output-file"), - fileSize = document.getElementById("output-file-size"); - - fileOverlay.style.display = "block"; - fileSize.textContent = file.size.toLocaleString() + " bytes"; - - // Display preview slice in the background - const outputText = document.getElementById("output-text"), - fileSlice = this.dishBuffer.slice(0, 4096); - - outputText.classList.add("blur"); - outputText.value = Utils.printable(Utils.arrayBufferToStr(fileSlice)); - } - - - /** - * Removes the output file and nulls its memory. - */ - closeFile() { - this.dishBuffer = null; - document.getElementById("output-file").style.display = "none"; - document.getElementById("output-text").classList.remove("blur"); - } - - - /** - * Handler for file download events. - */ - async downloadFile() { - this.filename = window.prompt("Please enter a filename:", this.filename || "download.dat"); - await this.getDishBuffer(); - const file = new File([this.dishBuffer], this.filename); - if (this.filename) FileSaver.saveAs(file, this.filename, false); - } - - - /** - * Handler for file slice display events. - */ - displayFileSlice() { - const startTime = new Date().getTime(), - showFileOverlay = document.getElementById("show-file-overlay"), - sliceFromEl = document.getElementById("output-file-slice-from"), - sliceToEl = document.getElementById("output-file-slice-to"), - sliceFrom = parseInt(sliceFromEl.value, 10), - sliceTo = parseInt(sliceToEl.value, 10), - str = Utils.arrayBufferToStr(this.dishBuffer.slice(sliceFrom, sliceTo)); - - document.getElementById("output-text").classList.remove("blur"); - showFileOverlay.style.display = "block"; - this.set(str, "string", new Date().getTime() - startTime, true); - } - - - /** - * Handler for show file overlay events. - * - * @param {Event} e - */ - showFileOverlayClick(e) { - const outputFile = document.getElementById("output-file"), - showFileOverlay = e.target; - - document.getElementById("output-text").classList.add("blur"); - outputFile.style.display = "block"; - showFileOverlay.style.display = "none"; - this.setOutputInfo(this.dishBuffer.byteLength, null, 0); - } - - - /** - * Displays information about the output. - * - * @param {number} length - The length of the current output string - * @param {number} lines - The number of the lines in the current output string - * @param {number} duration - The length of time (ms) it took to generate the output - */ - setOutputInfo(length, lines, duration) { - let width = length.toString().length; - width = width < 4 ? 4 : width; - - const lengthStr = length.toString().padStart(width, " ").replace(/ /g, " "); - const timeStr = (duration.toString() + "ms").padStart(width, " ").replace(/ /g, " "); - - let msg = "time: " + timeStr + "
length: " + lengthStr; - - if (typeof lines === "number") { - const linesStr = lines.toString().padStart(width, " ").replace(/ /g, " "); - msg += "
lines: " + linesStr; - } - - document.getElementById("output-info").innerHTML = msg; - document.getElementById("input-selection-info").innerHTML = ""; - document.getElementById("output-selection-info").innerHTML = ""; - } - - - /** - * Handler for save click events. - * Saves the current output to a file. - */ - saveClick() { - this.downloadFile(); - } - - - /** - * Handler for copy click events. - * Copies the output to the clipboard. - */ - async copyClick() { - await this.getDishStr(); - - // Create invisible textarea to populate with the raw dish string (not the printable version that - // contains dots instead of the actual bytes) - const textarea = document.createElement("textarea"); - textarea.style.position = "fixed"; - textarea.style.top = 0; - textarea.style.left = 0; - textarea.style.width = 0; - textarea.style.height = 0; - textarea.style.border = "none"; - - textarea.value = this.dishStr; - document.body.appendChild(textarea); - - // Select and copy the contents of this textarea - let success = false; - try { - textarea.select(); - success = this.dishStr && document.execCommand("copy"); - } catch (err) { - success = false; - } - - if (success) { - this.app.alert("Copied raw output successfully.", 2000); - } else { - this.app.alert("Sorry, the output could not be copied.", 3000); - } - - // Clean up - document.body.removeChild(textarea); - } - - - /** - * Handler for switch click events. - * Moves the current output into the input textarea. - */ - async switchClick() { - this.switchOrigData = this.manager.input.get(); - document.getElementById("undo-switch").disabled = false; - if (this.dishBuffer) { - this.manager.input.setFile(new File([this.dishBuffer], "output.dat")); - this.manager.input.handleLoaderMessage({ - data: { - progress: 100, - fileBuffer: this.dishBuffer - } - }); - } else { - await this.getDishStr(); - this.app.setInput(this.dishStr); - } - } - - - /** - * Handler for undo switch click events. - * Removes the output from the input and replaces the input that was removed. - */ - undoSwitchClick() { - this.app.setInput(this.switchOrigData); - const undoSwitch = document.getElementById("undo-switch"); - undoSwitch.disabled = true; - $(undoSwitch).tooltip("hide"); - } - - - /** - * Handler for maximise output click events. - * Resizes the output frame to be as large as possible, or restores it to its original size. - */ - maximiseOutputClick(e) { - 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); - - $(el).attr("data-original-title", "Restore output pane"); - el.querySelector("i").innerHTML = "fullscreen_exit"; - } else { - $(el).attr("data-original-title", "Maximise output pane"); - el.querySelector("i").innerHTML = "fullscreen"; - this.app.initialiseSplitter(false); - this.app.resetLayout(); - } - } - - - /** - * Save bombe object then remove it from the DOM so that it does not cause performance issues. - */ - saveBombe() { - this.bombeEl = document.getElementById("bombe"); - this.bombeEl.parentNode.removeChild(this.bombeEl); - } - - - /** - * Shows or hides the output loading screen. - * The animated Bombe SVG, whilst quite aesthetically pleasing, is reasonably CPU - * intensive, so we remove it from the DOM when not in use. We only show it if the - * recipe is taking longer than 200ms. We add it to the DOM just before that so that - * it is ready to fade in without stuttering. - * - * @param {boolean} value - true == show loader - */ - toggleLoader(value) { - clearTimeout(this.appendBombeTimeout); - clearTimeout(this.outputLoaderTimeout); - - const outputLoader = document.getElementById("output-loader"), - outputElement = document.getElementById("output-text"), - animation = document.getElementById("output-loader-animation"); - - if (value) { - this.manager.controls.hideStaleIndicator(); - - // Start a timer to add the Bombe to the DOM just before we make it - // visible so that there is no stuttering - this.appendBombeTimeout = setTimeout(function() { - animation.appendChild(this.bombeEl); - }.bind(this), 150); - - // Show the loading screen - this.outputLoaderTimeout = setTimeout(function() { - outputElement.disabled = true; - outputLoader.style.visibility = "visible"; - outputLoader.style.opacity = 1; - this.manager.controls.toggleBakeButtonFunction(true); - }.bind(this), 200); - } else { - // Remove the Bombe from the DOM to save resources - this.outputLoaderTimeout = setTimeout(function () { - try { - animation.removeChild(this.bombeEl); - } catch (err) {} - }.bind(this), 500); - outputElement.disabled = false; - outputLoader.style.opacity = 0; - outputLoader.style.visibility = "hidden"; - this.manager.controls.toggleBakeButtonFunction(false); - this.setStatusMsg(""); - } - } - - - /** - * Sets the baking status message value. - * - * @param {string} msg - */ - setStatusMsg(msg) { - const el = document.querySelector("#output-loader .loading-msg"); - - el.textContent = msg; - } - - - /** - * Returns true if the output contains carriage returns - * - * @returns {boolean} - */ - async containsCR() { - await this.getDishStr(); - return this.dishStr.indexOf("\r") >= 0; - } - - - /** - * Retrieves the current dish as a string, returning the cached version if possible. - * - * @returns {string} - */ - async getDishStr() { - if (this.dishStr) return this.dishStr; - - this.dishStr = await new Promise(resolve => { - this.manager.worker.getDishAs(this.app.dish, "string", r => { - resolve(r.value); - }); - }); - return this.dishStr; - } - - - /** - * Retrieves the current dish as an ArrayBuffer, returning the cached version if possible. - * - * @returns {ArrayBuffer} - */ - async getDishBuffer() { - if (this.dishBuffer) return this.dishBuffer; - - this.dishBuffer = await new Promise(resolve => { - this.manager.worker.getDishAs(this.app.dish, "ArrayBuffer", r => { - resolve(r.value); - }); - }); - return this.dishBuffer; - } - - - /** - * Triggers the BackgroundWorker to attempt Magic on the current output. - */ - backgroundMagic() { - this.hideMagicButton(); - if (!this.app.options.autoMagic) return; - - const sample = this.dishStr ? this.dishStr.slice(0, 1000) : - this.dishBuffer ? this.dishBuffer.slice(0, 1000) : ""; - - if (sample.length) { - this.manager.background.magic(sample); - } - } - - - /** - * Handles the results of a background Magic call. - * - * @param {Object[]} options - */ - backgroundMagicResult(options) { - if (!options.length || - !options[0].recipe.length) - return; - - const currentRecipeConfig = this.app.getRecipeConfig(); - const newRecipeConfig = currentRecipeConfig.concat(options[0].recipe); - const opSequence = options[0].recipe.map(o => o.op).join(", "); - - this.showMagicButton(opSequence, options[0].data, newRecipeConfig); - } - - - /** - * Handler for Magic click events. - * - * Loads the Magic recipe. - * - * @fires Manager#statechange - */ - magicClick() { - const magicButton = document.getElementById("magic"); - this.app.setRecipeConfig(JSON.parse(magicButton.getAttribute("data-recipe"))); - window.dispatchEvent(this.manager.statechange); - this.hideMagicButton(); - } - - - /** - * Displays the Magic button with a title and adds a link to a complete recipe. - * - * @param {string} opSequence - * @param {string} result - * @param {Object[]} recipeConfig - */ - showMagicButton(opSequence, result, recipeConfig) { - const magicButton = document.getElementById("magic"); - magicButton.setAttribute("data-original-title", `${opSequence} will produce "${Utils.escapeHtml(Utils.truncate(result), 30)}"`); - magicButton.setAttribute("data-recipe", JSON.stringify(recipeConfig), null, ""); - magicButton.classList.remove("hidden"); - } - - - /** - * Hides the Magic button and resets its values. - */ - hideMagicButton() { - const magicButton = document.getElementById("magic"); - magicButton.classList.add("hidden"); - magicButton.setAttribute("data-recipe", ""); - magicButton.setAttribute("data-original-title", "Magic!"); - } - - - /** - * Handler for extract file events. - * - * @param {Event} e - */ - async extractFileClick(e) { - e.preventDefault(); - e.stopPropagation(); - - const el = e.target.nodeName === "I" ? e.target.parentNode : e.target; - const blobURL = el.getAttribute("blob-url"); - const fileName = el.getAttribute("file-name"); - - const blob = await fetch(blobURL).then(r => r.blob()); - this.manager.input.loadFile(new File([blob], fileName, {type: blob.type})); - } - -} - -export default OutputWaiter; diff --git a/src/web/WorkerWaiter.mjs b/src/web/WorkerWaiter.mjs deleted file mode 100755 index 7ef72263..00000000 --- a/src/web/WorkerWaiter.mjs +++ /dev/null @@ -1,239 +0,0 @@ -/** - * @author n1474335 [n1474335@gmail.com] - * @copyright Crown Copyright 2017 - * @license Apache-2.0 - */ - -import ChefWorker from "worker-loader?inline&fallback=false!../core/ChefWorker"; - -/** - * Waiter to handle conversations with the ChefWorker. - */ -class WorkerWaiter { - - /** - * WorkerWaiter constructor. - * - * @param {App} app - The main view object for CyberChef. - * @param {Manager} manager - The CyberChef event manager. - */ - constructor(app, manager) { - this.app = app; - this.manager = manager; - - this.callbacks = {}; - this.callbackID = 0; - } - - - /** - * Sets up the ChefWorker and associated listeners. - */ - registerChefWorker() { - log.debug("Registering new ChefWorker"); - this.chefWorker = new ChefWorker(); - this.chefWorker.addEventListener("message", this.handleChefMessage.bind(this)); - this.setLogLevel(); - - let docURL = document.location.href.split(/[#?]/)[0]; - const index = docURL.lastIndexOf("/"); - if (index > 0) { - docURL = docURL.substring(0, index); - } - this.chefWorker.postMessage({"action": "docURL", "data": docURL}); - } - - - /** - * Handler for messages sent back by the ChefWorker. - * - * @param {MessageEvent} e - */ - handleChefMessage(e) { - const r = e.data; - log.debug("Receiving '" + r.action + "' from ChefWorker"); - - switch (r.action) { - case "bakeComplete": - this.bakingComplete(r.data); - break; - case "bakeError": - this.app.handleError(r.data); - this.setBakingStatus(false); - break; - case "dishReturned": - this.callbacks[r.data.id](r.data); - break; - case "silentBakeComplete": - break; - case "workerLoaded": - this.app.workerLoaded = true; - log.debug("ChefWorker loaded"); - this.app.loaded(); - break; - case "statusMessage": - this.manager.output.setStatusMsg(r.data); - break; - case "optionUpdate": - log.debug(`Setting ${r.data.option} to ${r.data.value}`); - this.app.options[r.data.option] = r.data.value; - break; - case "setRegisters": - this.manager.recipe.setRegisters(r.data.opIndex, r.data.numPrevRegisters, r.data.registers); - break; - case "highlightsCalculated": - this.manager.highlighter.displayHighlights(r.data.pos, r.data.direction); - break; - default: - log.error("Unrecognised message from ChefWorker", e); - break; - } - } - - - /** - * Updates the UI to show if baking is in process or not. - * - * @param {bakingStatus} - */ - setBakingStatus(bakingStatus) { - this.app.baking = bakingStatus; - - this.manager.output.toggleLoader(bakingStatus); - } - - - /** - * Cancels the current bake by terminating the ChefWorker and creating a new one. - */ - cancelBake() { - this.chefWorker.terminate(); - this.registerChefWorker(); - this.setBakingStatus(false); - this.manager.controls.showStaleIndicator(); - } - - - /** - * Handler for completed bakes. - * - * @param {Object} response - */ - bakingComplete(response) { - this.setBakingStatus(false); - - if (!response) return; - - if (response.error) { - this.app.handleError(response.error); - } - - this.app.progress = response.progress; - this.app.dish = response.dish; - this.manager.recipe.updateBreakpointIndicator(response.progress); - this.manager.output.set(response.result, response.type, response.duration); - log.debug("--- Bake complete ---"); - } - - - /** - * Asks the ChefWorker to bake the current input using the current recipe. - * - * @param {string} input - * @param {Object[]} recipeConfig - * @param {Object} options - * @param {number} progress - * @param {boolean} step - */ - bake(input, recipeConfig, options, progress, step) { - this.setBakingStatus(true); - - this.chefWorker.postMessage({ - action: "bake", - data: { - input: input, - recipeConfig: recipeConfig, - options: options, - progress: progress, - step: step - } - }); - } - - - /** - * Asks the ChefWorker to run a silent bake, forcing the browser to load and cache all the relevant - * JavaScript code needed to do a real bake. - * - * @param {Object[]} [recipeConfig] - */ - silentBake(recipeConfig) { - this.chefWorker.postMessage({ - action: "silentBake", - data: { - recipeConfig: recipeConfig - } - }); - } - - - /** - * Asks the ChefWorker to calculate highlight offsets if possible. - * - * @param {Object[]} recipeConfig - * @param {string} direction - * @param {Object} pos - The position object for the highlight. - * @param {number} pos.start - The start offset. - * @param {number} pos.end - The end offset. - */ - highlight(recipeConfig, direction, pos) { - this.chefWorker.postMessage({ - action: "highlight", - data: { - recipeConfig: recipeConfig, - direction: direction, - pos: pos - } - }); - } - - - /** - * Asks the ChefWorker to return the dish as the specified type - * - * @param {Dish} dish - * @param {string} type - * @param {Function} callback - */ - getDishAs(dish, type, callback) { - const id = this.callbackID++; - this.callbacks[id] = callback; - this.chefWorker.postMessage({ - action: "getDishAs", - data: { - dish: dish, - type: type, - id: id - } - }); - } - - - /** - * Sets the console log level in the worker. - * - * @param {string} level - */ - setLogLevel(level) { - if (!this.chefWorker) return; - - this.chefWorker.postMessage({ - action: "setLogLevel", - data: log.getLevel() - }); - } - -} - - -export default WorkerWaiter; diff --git a/src/web/html/index.html b/src/web/html/index.html index 700cc24c..7fcb7415 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -191,7 +191,7 @@
    -
    +
    @@ -218,9 +218,16 @@
    - + + +
    -
    + +
    - -
    -
    + +
    +
    - +
    Name:
    @@ -257,13 +289,16 @@
    + - +
    -
    -
    -
    - - -
    -
    -
    -
    - -
    - Size:
    - -
    - - - - -
    to
    - +
    + +
    +
    +
    + + +
    +
    +
    +
    + +
    + Size:
    + +
    + + + + +
    to
    + +
    -
    -
    -
    - +
    +
    + +
    +
    -
    @@ -425,6 +484,8 @@ + +
    @@ -498,6 +559,20 @@ Attempt to detect encoded data automagically
    + +
    + +
    + +
    + +
    + + + diff --git a/src/web/index.js b/src/web/index.js index cbf7bccc..1d2bc242 100755 --- a/src/web/index.js +++ b/src/web/index.js @@ -10,13 +10,13 @@ import "./stylesheets/index.js"; // Libs import "arrive"; import "snackbarjs"; -import "bootstrap-material-design"; +import "bootstrap-material-design/js/index"; import "bootstrap-colorpicker"; import moment from "moment-timezone"; -import * as CanvasComponents from "../core/lib/CanvasComponents"; +import * as CanvasComponents from "../core/lib/CanvasComponents.mjs"; // CyberChef -import App from "./App"; +import App from "./App.mjs"; import Categories from "../core/config/Categories.json"; import OperationConfig from "../core/config/OperationConfig.json"; @@ -52,6 +52,8 @@ function main() { ioDisplayThreshold: 512, logLevel: "info", autoMagic: true, + imagePreview: true, + syncTabs: true }; document.removeEventListener("DOMContentLoaded", main, false); diff --git a/src/web/static/fonts/bmfonts/Roboto72White.fnt b/src/web/static/fonts/bmfonts/Roboto72White.fnt new file mode 100644 index 00000000..57238158 --- /dev/null +++ b/src/web/static/fonts/bmfonts/Roboto72White.fnt @@ -0,0 +1,485 @@ +info face="Roboto" size=72 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2 +common lineHeight=85 base=67 scaleW=512 scaleH=512 pages=1 packed=0 +page id=0 file="Roboto72White.png" +chars count=98 +char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=0 page=0 chnl=0 +char id=10 x=0 y=0 width=70 height=99 xoffset=2 yoffset=-11 xadvance=74 page=0 chnl=0 +char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=18 page=0 chnl=0 +char id=33 x=493 y=99 width=10 height=55 xoffset=5 yoffset=14 xadvance=19 page=0 chnl=0 +char id=34 x=446 y=319 width=16 height=19 xoffset=4 yoffset=12 xadvance=23 page=0 chnl=0 +char id=35 x=204 y=265 width=41 height=54 xoffset=3 yoffset=14 xadvance=44 page=0 chnl=0 +char id=36 x=269 y=0 width=35 height=69 xoffset=3 yoffset=6 xadvance=40 page=0 chnl=0 +char id=37 x=31 y=155 width=48 height=56 xoffset=3 yoffset=13 xadvance=53 page=0 chnl=0 +char id=38 x=79 y=155 width=43 height=56 xoffset=3 yoffset=13 xadvance=45 page=0 chnl=0 +char id=39 x=503 y=99 width=7 height=19 xoffset=3 yoffset=12 xadvance=13 page=0 chnl=0 +char id=40 x=70 y=0 width=21 height=78 xoffset=4 yoffset=7 xadvance=25 page=0 chnl=0 +char id=41 x=91 y=0 width=22 height=78 xoffset=-1 yoffset=7 xadvance=25 page=0 chnl=0 +char id=42 x=342 y=319 width=32 height=32 xoffset=-1 yoffset=14 xadvance=31 page=0 chnl=0 +char id=43 x=242 y=319 width=37 height=40 xoffset=2 yoffset=23 xadvance=41 page=0 chnl=0 +char id=44 x=433 y=319 width=13 height=21 xoffset=-1 yoffset=58 xadvance=14 page=0 chnl=0 +char id=45 x=27 y=360 width=19 height=8 xoffset=0 yoffset=41 xadvance=19 page=0 chnl=0 +char id=46 x=17 y=360 width=10 height=11 xoffset=4 yoffset=58 xadvance=19 page=0 chnl=0 +char id=47 x=355 y=0 width=30 height=58 xoffset=-1 yoffset=14 xadvance=30 page=0 chnl=0 +char id=48 x=449 y=99 width=34 height=56 xoffset=3 yoffset=13 xadvance=40 page=0 chnl=0 +char id=49 x=474 y=211 width=22 height=54 xoffset=5 yoffset=14 xadvance=40 page=0 chnl=0 +char id=50 x=195 y=155 width=37 height=55 xoffset=2 yoffset=13 xadvance=41 page=0 chnl=0 +char id=51 x=379 y=99 width=35 height=56 xoffset=2 yoffset=13 xadvance=40 page=0 chnl=0 +char id=52 x=128 y=265 width=39 height=54 xoffset=1 yoffset=14 xadvance=41 page=0 chnl=0 +char id=53 x=232 y=155 width=35 height=55 xoffset=4 yoffset=14 xadvance=40 page=0 chnl=0 +char id=54 x=267 y=155 width=35 height=55 xoffset=4 yoffset=14 xadvance=41 page=0 chnl=0 +char id=55 x=167 y=265 width=37 height=54 xoffset=2 yoffset=14 xadvance=41 page=0 chnl=0 +char id=56 x=414 y=99 width=35 height=56 xoffset=3 yoffset=13 xadvance=40 page=0 chnl=0 +char id=57 x=302 y=155 width=34 height=55 xoffset=3 yoffset=13 xadvance=41 page=0 chnl=0 +char id=58 x=495 y=265 width=10 height=41 xoffset=4 yoffset=28 xadvance=18 page=0 chnl=0 +char id=59 x=496 y=211 width=13 height=52 xoffset=0 yoffset=28 xadvance=15 page=0 chnl=0 +char id=60 x=279 y=319 width=31 height=35 xoffset=2 yoffset=27 xadvance=37 page=0 chnl=0 +char id=61 x=402 y=319 width=31 height=23 xoffset=4 yoffset=31 xadvance=39 page=0 chnl=0 +char id=62 x=310 y=319 width=32 height=35 xoffset=4 yoffset=27 xadvance=38 page=0 chnl=0 +char id=63 x=0 y=155 width=31 height=56 xoffset=2 yoffset=13 xadvance=34 page=0 chnl=0 +char id=64 x=210 y=0 width=59 height=69 xoffset=3 yoffset=15 xadvance=65 page=0 chnl=0 +char id=65 x=336 y=155 width=49 height=54 xoffset=-1 yoffset=14 xadvance=47 page=0 chnl=0 +char id=66 x=385 y=155 width=37 height=54 xoffset=5 yoffset=14 xadvance=45 page=0 chnl=0 +char id=67 x=0 y=99 width=42 height=56 xoffset=3 yoffset=13 xadvance=46 page=0 chnl=0 +char id=68 x=422 y=155 width=39 height=54 xoffset=5 yoffset=14 xadvance=47 page=0 chnl=0 +char id=69 x=461 y=155 width=35 height=54 xoffset=5 yoffset=14 xadvance=41 page=0 chnl=0 +char id=70 x=0 y=211 width=34 height=54 xoffset=5 yoffset=14 xadvance=40 page=0 chnl=0 +char id=71 x=42 y=99 width=42 height=56 xoffset=3 yoffset=13 xadvance=49 page=0 chnl=0 +char id=72 x=34 y=211 width=41 height=54 xoffset=5 yoffset=14 xadvance=51 page=0 chnl=0 +char id=73 x=496 y=155 width=9 height=54 xoffset=5 yoffset=14 xadvance=19 page=0 chnl=0 +char id=74 x=122 y=155 width=34 height=55 xoffset=1 yoffset=14 xadvance=40 page=0 chnl=0 +char id=75 x=75 y=211 width=41 height=54 xoffset=5 yoffset=14 xadvance=45 page=0 chnl=0 +char id=76 x=116 y=211 width=33 height=54 xoffset=5 yoffset=14 xadvance=39 page=0 chnl=0 +char id=77 x=149 y=211 width=53 height=54 xoffset=5 yoffset=14 xadvance=63 page=0 chnl=0 +char id=78 x=202 y=211 width=41 height=54 xoffset=5 yoffset=14 xadvance=51 page=0 chnl=0 +char id=79 x=84 y=99 width=43 height=56 xoffset=3 yoffset=13 xadvance=49 page=0 chnl=0 +char id=80 x=243 y=211 width=39 height=54 xoffset=5 yoffset=14 xadvance=45 page=0 chnl=0 +char id=81 x=304 y=0 width=44 height=64 xoffset=3 yoffset=13 xadvance=49 page=0 chnl=0 +char id=82 x=282 y=211 width=40 height=54 xoffset=5 yoffset=14 xadvance=45 page=0 chnl=0 +char id=83 x=127 y=99 width=39 height=56 xoffset=2 yoffset=13 xadvance=43 page=0 chnl=0 +char id=84 x=322 y=211 width=42 height=54 xoffset=1 yoffset=14 xadvance=44 page=0 chnl=0 +char id=85 x=156 y=155 width=39 height=55 xoffset=4 yoffset=14 xadvance=47 page=0 chnl=0 +char id=86 x=364 y=211 width=47 height=54 xoffset=-1 yoffset=14 xadvance=46 page=0 chnl=0 +char id=87 x=411 y=211 width=63 height=54 xoffset=1 yoffset=14 xadvance=64 page=0 chnl=0 +char id=88 x=0 y=265 width=44 height=54 xoffset=1 yoffset=14 xadvance=45 page=0 chnl=0 +char id=89 x=44 y=265 width=45 height=54 xoffset=-1 yoffset=14 xadvance=43 page=0 chnl=0 +char id=90 x=89 y=265 width=39 height=54 xoffset=2 yoffset=14 xadvance=43 page=0 chnl=0 +char id=91 x=161 y=0 width=16 height=72 xoffset=4 yoffset=7 xadvance=19 page=0 chnl=0 +char id=92 x=385 y=0 width=30 height=58 xoffset=0 yoffset=14 xadvance=30 page=0 chnl=0 +char id=93 x=177 y=0 width=16 height=72 xoffset=0 yoffset=7 xadvance=20 page=0 chnl=0 +char id=94 x=374 y=319 width=28 height=28 xoffset=1 yoffset=14 xadvance=30 page=0 chnl=0 +char id=95 x=46 y=360 width=34 height=8 xoffset=0 yoffset=65 xadvance=34 page=0 chnl=0 +char id=96 x=0 y=360 width=17 height=13 xoffset=1 yoffset=11 xadvance=22 page=0 chnl=0 +char id=97 x=268 y=265 width=34 height=42 xoffset=3 yoffset=27 xadvance=39 page=0 chnl=0 +char id=98 x=415 y=0 width=34 height=57 xoffset=4 yoffset=12 xadvance=40 page=0 chnl=0 +char id=99 x=302 y=265 width=34 height=42 xoffset=2 yoffset=27 xadvance=38 page=0 chnl=0 +char id=100 x=449 y=0 width=34 height=57 xoffset=2 yoffset=12 xadvance=40 page=0 chnl=0 +char id=101 x=336 y=265 width=34 height=42 xoffset=2 yoffset=27 xadvance=38 page=0 chnl=0 +char id=102 x=483 y=0 width=25 height=57 xoffset=1 yoffset=11 xadvance=26 page=0 chnl=0 +char id=103 x=166 y=99 width=34 height=56 xoffset=2 yoffset=27 xadvance=40 page=0 chnl=0 +char id=104 x=200 y=99 width=32 height=56 xoffset=4 yoffset=12 xadvance=40 page=0 chnl=0 +char id=105 x=483 y=99 width=10 height=55 xoffset=4 yoffset=13 xadvance=18 page=0 chnl=0 +char id=106 x=193 y=0 width=17 height=71 xoffset=-4 yoffset=13 xadvance=17 page=0 chnl=0 +char id=107 x=232 y=99 width=34 height=56 xoffset=4 yoffset=12 xadvance=37 page=0 chnl=0 +char id=108 x=266 y=99 width=9 height=56 xoffset=4 yoffset=12 xadvance=17 page=0 chnl=0 +char id=109 x=439 y=265 width=56 height=41 xoffset=4 yoffset=27 xadvance=64 page=0 chnl=0 +char id=110 x=0 y=319 width=32 height=41 xoffset=4 yoffset=27 xadvance=40 page=0 chnl=0 +char id=111 x=370 y=265 width=37 height=42 xoffset=2 yoffset=27 xadvance=41 page=0 chnl=0 +char id=112 x=275 y=99 width=34 height=56 xoffset=4 yoffset=27 xadvance=40 page=0 chnl=0 +char id=113 x=309 y=99 width=34 height=56 xoffset=2 yoffset=27 xadvance=41 page=0 chnl=0 +char id=114 x=32 y=319 width=21 height=41 xoffset=4 yoffset=27 xadvance=25 page=0 chnl=0 +char id=115 x=407 y=265 width=32 height=42 xoffset=2 yoffset=27 xadvance=37 page=0 chnl=0 +char id=116 x=245 y=265 width=23 height=51 xoffset=0 yoffset=18 xadvance=25 page=0 chnl=0 +char id=117 x=53 y=319 width=32 height=41 xoffset=4 yoffset=28 xadvance=40 page=0 chnl=0 +char id=118 x=85 y=319 width=35 height=40 xoffset=0 yoffset=28 xadvance=35 page=0 chnl=0 +char id=119 x=120 y=319 width=54 height=40 xoffset=0 yoffset=28 xadvance=54 page=0 chnl=0 +char id=120 x=174 y=319 width=36 height=40 xoffset=0 yoffset=28 xadvance=36 page=0 chnl=0 +char id=121 x=343 y=99 width=36 height=56 xoffset=-1 yoffset=28 xadvance=34 page=0 chnl=0 +char id=122 x=210 y=319 width=32 height=40 xoffset=2 yoffset=28 xadvance=35 page=0 chnl=0 +char id=123 x=113 y=0 width=24 height=73 xoffset=1 yoffset=9 xadvance=25 page=0 chnl=0 +char id=124 x=348 y=0 width=7 height=63 xoffset=5 yoffset=14 xadvance=17 page=0 chnl=0 +char id=125 x=137 y=0 width=24 height=73 xoffset=-1 yoffset=9 xadvance=24 page=0 chnl=0 +char id=126 x=462 y=319 width=42 height=16 xoffset=4 yoffset=38 xadvance=50 page=0 chnl=0 +char id=127 x=0 y=0 width=70 height=99 xoffset=2 yoffset=-11 xadvance=74 page=0 chnl=0 +kernings count=382 +kerning first=70 second=74 amount=-9 +kerning first=34 second=97 amount=-2 +kerning first=34 second=101 amount=-2 +kerning first=34 second=113 amount=-2 +kerning first=34 second=99 amount=-2 +kerning first=70 second=99 amount=-1 +kerning first=88 second=113 amount=-1 +kerning first=84 second=46 amount=-8 +kerning first=84 second=119 amount=-2 +kerning first=87 second=97 amount=-1 +kerning first=90 second=117 amount=-1 +kerning first=39 second=97 amount=-2 +kerning first=69 second=111 amount=-1 +kerning first=87 second=41 amount=1 +kerning first=76 second=86 amount=-6 +kerning first=121 second=34 amount=1 +kerning first=40 second=86 amount=1 +kerning first=85 second=65 amount=-1 +kerning first=89 second=89 amount=1 +kerning first=72 second=65 amount=1 +kerning first=104 second=39 amount=-4 +kerning first=114 second=102 amount=1 +kerning first=89 second=42 amount=-2 +kerning first=114 second=34 amount=1 +kerning first=84 second=115 amount=-4 +kerning first=84 second=71 amount=-1 +kerning first=89 second=101 amount=-2 +kerning first=89 second=45 amount=-2 +kerning first=122 second=99 amount=-1 +kerning first=78 second=88 amount=1 +kerning first=68 second=89 amount=-2 +kerning first=122 second=103 amount=-1 +kerning first=78 second=84 amount=-1 +kerning first=86 second=103 amount=-2 +kerning first=89 second=67 amount=-1 +kerning first=89 second=79 amount=-1 +kerning first=75 second=111 amount=-1 +kerning first=111 second=120 amount=-1 +kerning first=87 second=44 amount=-4 +kerning first=91 second=74 amount=-1 +kerning first=120 second=111 amount=-1 +kerning first=84 second=111 amount=-3 +kerning first=102 second=113 amount=-1 +kerning first=80 second=88 amount=-1 +kerning first=66 second=84 amount=-1 +kerning first=65 second=87 amount=-2 +kerning first=86 second=100 amount=-2 +kerning first=122 second=100 amount=-1 +kerning first=75 second=118 amount=-1 +kerning first=70 second=118 amount=-1 +kerning first=73 second=88 amount=1 +kerning first=70 second=121 amount=-1 +kerning first=65 second=34 amount=-4 +kerning first=39 second=101 amount=-2 +kerning first=75 second=101 amount=-1 +kerning first=84 second=99 amount=-3 +kerning first=84 second=65 amount=-3 +kerning first=112 second=39 amount=-1 +kerning first=76 second=39 amount=-12 +kerning first=78 second=65 amount=1 +kerning first=88 second=45 amount=-2 +kerning first=65 second=121 amount=-2 +kerning first=34 second=111 amount=-2 +kerning first=89 second=85 amount=-3 +kerning first=114 second=99 amount=-1 +kerning first=86 second=125 amount=1 +kerning first=70 second=111 amount=-1 +kerning first=89 second=120 amount=-1 +kerning first=90 second=119 amount=-1 +kerning first=120 second=99 amount=-1 +kerning first=89 second=117 amount=-1 +kerning first=82 second=89 amount=-2 +kerning first=75 second=117 amount=-1 +kerning first=34 second=34 amount=-4 +kerning first=89 second=110 amount=-1 +kerning first=88 second=101 amount=-1 +kerning first=107 second=103 amount=-1 +kerning first=34 second=115 amount=-3 +kerning first=98 second=39 amount=-1 +kerning first=70 second=65 amount=-6 +kerning first=70 second=46 amount=-8 +kerning first=98 second=34 amount=-1 +kerning first=70 second=84 amount=1 +kerning first=114 second=100 amount=-1 +kerning first=88 second=79 amount=-1 +kerning first=39 second=113 amount=-2 +kerning first=114 second=103 amount=-1 +kerning first=77 second=65 amount=1 +kerning first=120 second=103 amount=-1 +kerning first=114 second=121 amount=1 +kerning first=89 second=100 amount=-2 +kerning first=80 second=65 amount=-5 +kerning first=121 second=111 amount=-1 +kerning first=84 second=74 amount=-8 +kerning first=122 second=111 amount=-1 +kerning first=114 second=118 amount=1 +kerning first=102 second=41 amount=1 +kerning first=122 second=113 amount=-1 +kerning first=89 second=122 amount=-1 +kerning first=89 second=38 amount=-1 +kerning first=81 second=89 amount=-1 +kerning first=114 second=111 amount=-1 +kerning first=46 second=34 amount=-6 +kerning first=84 second=112 amount=-4 +kerning first=112 second=34 amount=-1 +kerning first=76 second=34 amount=-12 +kerning first=102 second=125 amount=1 +kerning first=39 second=115 amount=-3 +kerning first=76 second=118 amount=-5 +kerning first=86 second=99 amount=-2 +kerning first=84 second=84 amount=1 +kerning first=86 second=65 amount=-3 +kerning first=87 second=101 amount=-1 +kerning first=67 second=125 amount=-1 +kerning first=120 second=113 amount=-1 +kerning first=118 second=46 amount=-4 +kerning first=88 second=103 amount=-1 +kerning first=111 second=122 amount=-1 +kerning first=77 second=84 amount=-1 +kerning first=114 second=46 amount=-4 +kerning first=34 second=39 amount=-4 +kerning first=114 second=44 amount=-4 +kerning first=69 second=84 amount=1 +kerning first=89 second=46 amount=-7 +kerning first=97 second=39 amount=-2 +kerning first=34 second=100 amount=-2 +kerning first=70 second=100 amount=-1 +kerning first=84 second=120 amount=-3 +kerning first=90 second=118 amount=-1 +kerning first=70 second=114 amount=-1 +kerning first=34 second=112 amount=-1 +kerning first=109 second=34 amount=-4 +kerning first=86 second=113 amount=-2 +kerning first=88 second=71 amount=-1 +kerning first=66 second=89 amount=-2 +kerning first=102 second=103 amount=-1 +kerning first=88 second=67 amount=-1 +kerning first=39 second=110 amount=-1 +kerning first=75 second=110 amount=-1 +kerning first=88 second=117 amount=-1 +kerning first=89 second=118 amount=-1 +kerning first=97 second=118 amount=-1 +kerning first=87 second=65 amount=-2 +kerning first=73 second=89 amount=-1 +kerning first=89 second=74 amount=-3 +kerning first=102 second=101 amount=-1 +kerning first=86 second=111 amount=-2 +kerning first=65 second=119 amount=-1 +kerning first=84 second=100 amount=-3 +kerning first=104 second=34 amount=-4 +kerning first=86 second=41 amount=1 +kerning first=111 second=34 amount=-5 +kerning first=40 second=89 amount=1 +kerning first=121 second=39 amount=1 +kerning first=68 second=90 amount=-1 +kerning first=114 second=113 amount=-1 +kerning first=68 second=88 amount=-1 +kerning first=98 second=120 amount=-1 +kerning first=110 second=34 amount=-4 +kerning first=119 second=44 amount=-4 +kerning first=119 second=46 amount=-4 +kerning first=118 second=44 amount=-4 +kerning first=84 second=114 amount=-3 +kerning first=86 second=97 amount=-2 +kerning first=68 second=86 amount=-1 +kerning first=86 second=93 amount=1 +kerning first=97 second=34 amount=-2 +kerning first=34 second=65 amount=-4 +kerning first=84 second=118 amount=-3 +kerning first=76 second=84 amount=-10 +kerning first=107 second=99 amount=-1 +kerning first=121 second=46 amount=-4 +kerning first=123 second=85 amount=-1 +kerning first=65 second=63 amount=-2 +kerning first=89 second=44 amount=-7 +kerning first=80 second=118 amount=1 +kerning first=112 second=122 amount=-1 +kerning first=79 second=65 amount=-1 +kerning first=80 second=121 amount=1 +kerning first=118 second=34 amount=1 +kerning first=87 second=45 amount=-2 +kerning first=69 second=100 amount=-1 +kerning first=87 second=103 amount=-1 +kerning first=112 second=120 amount=-1 +kerning first=68 second=44 amount=-4 +kerning first=86 second=45 amount=-1 +kerning first=39 second=34 amount=-4 +kerning first=68 second=46 amount=-4 +kerning first=65 second=89 amount=-3 +kerning first=69 second=118 amount=-1 +kerning first=88 second=99 amount=-1 +kerning first=87 second=46 amount=-4 +kerning first=47 second=47 amount=-8 +kerning first=73 second=65 amount=1 +kerning first=123 second=74 amount=-1 +kerning first=69 second=102 amount=-1 +kerning first=87 second=111 amount=-1 +kerning first=39 second=112 amount=-1 +kerning first=89 second=116 amount=-1 +kerning first=70 second=113 amount=-1 +kerning first=77 second=88 amount=1 +kerning first=84 second=32 amount=-1 +kerning first=90 second=103 amount=-1 +kerning first=65 second=86 amount=-3 +kerning first=75 second=112 amount=-1 +kerning first=39 second=109 amount=-1 +kerning first=75 second=81 amount=-1 +kerning first=89 second=115 amount=-2 +kerning first=84 second=83 amount=-1 +kerning first=89 second=87 amount=1 +kerning first=114 second=101 amount=-1 +kerning first=116 second=111 amount=-1 +kerning first=90 second=100 amount=-1 +kerning first=84 second=122 amount=-2 +kerning first=68 second=84 amount=-1 +kerning first=32 second=84 amount=-1 +kerning first=84 second=117 amount=-3 +kerning first=74 second=65 amount=-1 +kerning first=107 second=101 amount=-1 +kerning first=75 second=109 amount=-1 +kerning first=80 second=46 amount=-11 +kerning first=89 second=93 amount=1 +kerning first=89 second=65 amount=-3 +kerning first=87 second=117 amount=-1 +kerning first=89 second=81 amount=-1 +kerning first=39 second=103 amount=-2 +kerning first=86 second=101 amount=-2 +kerning first=86 second=117 amount=-1 +kerning first=84 second=113 amount=-3 +kerning first=34 second=110 amount=-1 +kerning first=89 second=84 amount=1 +kerning first=84 second=110 amount=-4 +kerning first=39 second=99 amount=-2 +kerning first=88 second=121 amount=-1 +kerning first=65 second=39 amount=-4 +kerning first=110 second=39 amount=-4 +kerning first=75 second=67 amount=-1 +kerning first=88 second=118 amount=-1 +kerning first=86 second=114 amount=-1 +kerning first=80 second=74 amount=-7 +kerning first=84 second=97 amount=-4 +kerning first=82 second=84 amount=-3 +kerning first=91 second=85 amount=-1 +kerning first=102 second=99 amount=-1 +kerning first=66 second=86 amount=-1 +kerning first=120 second=101 amount=-1 +kerning first=102 second=93 amount=1 +kerning first=75 second=100 amount=-1 +kerning first=84 second=79 amount=-1 +kerning first=111 second=121 amount=-1 +kerning first=75 second=121 amount=-1 +kerning first=81 second=87 amount=-1 +kerning first=107 second=113 amount=-1 +kerning first=120 second=100 amount=-1 +kerning first=90 second=79 amount=-1 +kerning first=89 second=114 amount=-1 +kerning first=122 second=101 amount=-1 +kerning first=111 second=118 amount=-1 +kerning first=82 second=86 amount=-1 +kerning first=67 second=84 amount=-1 +kerning first=70 second=101 amount=-1 +kerning first=89 second=83 amount=-1 +kerning first=114 second=97 amount=-1 +kerning first=70 second=97 amount=-1 +kerning first=89 second=102 amount=-1 +kerning first=78 second=89 amount=-1 +kerning first=70 second=44 amount=-8 +kerning first=44 second=39 amount=-6 +kerning first=84 second=45 amount=-8 +kerning first=89 second=121 amount=-1 +kerning first=84 second=86 amount=1 +kerning first=87 second=99 amount=-1 +kerning first=98 second=122 amount=-1 +kerning first=89 second=112 amount=-1 +kerning first=89 second=103 amount=-2 +kerning first=88 second=81 amount=-1 +kerning first=102 second=34 amount=1 +kerning first=109 second=39 amount=-4 +kerning first=81 second=84 amount=-2 +kerning first=121 second=97 amount=-1 +kerning first=89 second=99 amount=-2 +kerning first=89 second=125 amount=1 +kerning first=81 second=86 amount=-1 +kerning first=114 second=116 amount=2 +kerning first=114 second=119 amount=1 +kerning first=84 second=44 amount=-8 +kerning first=102 second=39 amount=1 +kerning first=44 second=34 amount=-6 +kerning first=34 second=109 amount=-1 +kerning first=75 second=119 amount=-2 +kerning first=76 second=65 amount=1 +kerning first=84 second=81 amount=-1 +kerning first=76 second=121 amount=-5 +kerning first=69 second=101 amount=-1 +kerning first=89 second=111 amount=-2 +kerning first=80 second=90 amount=-1 +kerning first=89 second=97 amount=-3 +kerning first=89 second=109 amount=-1 +kerning first=90 second=99 amount=-1 +kerning first=89 second=86 amount=1 +kerning first=79 second=88 amount=-1 +kerning first=70 second=103 amount=-1 +kerning first=34 second=103 amount=-2 +kerning first=84 second=67 amount=-1 +kerning first=76 second=79 amount=-2 +kerning first=89 second=41 amount=1 +kerning first=65 second=118 amount=-2 +kerning first=75 second=71 amount=-1 +kerning first=76 second=87 amount=-5 +kerning first=77 second=89 amount=-1 +kerning first=90 second=113 amount=-1 +kerning first=79 second=89 amount=-2 +kerning first=118 second=111 amount=-1 +kerning first=118 second=97 amount=-1 +kerning first=88 second=100 amount=-1 +kerning first=90 second=121 amount=-1 +kerning first=89 second=113 amount=-2 +kerning first=84 second=87 amount=1 +kerning first=39 second=111 amount=-2 +kerning first=80 second=44 amount=-11 +kerning first=39 second=100 amount=-2 +kerning first=75 second=113 amount=-1 +kerning first=88 second=111 amount=-1 +kerning first=84 second=89 amount=1 +kerning first=84 second=103 amount=-3 +kerning first=70 second=117 amount=-1 +kerning first=67 second=41 amount=-1 +kerning first=89 second=71 amount=-1 +kerning first=121 second=44 amount=-4 +kerning first=97 second=121 amount=-1 +kerning first=87 second=113 amount=-1 +kerning first=73 second=84 amount=-1 +kerning first=84 second=101 amount=-3 +kerning first=75 second=99 amount=-1 +kerning first=65 second=85 amount=-1 +kerning first=76 second=67 amount=-2 +kerning first=76 second=81 amount=-2 +kerning first=75 second=79 amount=-1 +kerning first=39 second=65 amount=-4 +kerning first=76 second=117 amount=-2 +kerning first=65 second=84 amount=-5 +kerning first=90 second=101 amount=-1 +kerning first=84 second=121 amount=-3 +kerning first=69 second=99 amount=-1 +kerning first=114 second=39 amount=1 +kerning first=84 second=109 amount=-4 +kerning first=76 second=119 amount=-3 +kerning first=76 second=85 amount=-2 +kerning first=65 second=116 amount=-1 +kerning first=76 second=71 amount=-2 +kerning first=79 second=90 amount=-1 +kerning first=107 second=100 amount=-1 +kerning first=90 second=111 amount=-1 +kerning first=79 second=44 amount=-4 +kerning first=75 second=45 amount=-2 +kerning first=40 second=87 amount=1 +kerning first=79 second=86 amount=-1 +kerning first=102 second=100 amount=-1 +kerning first=72 second=89 amount=-1 +kerning first=72 second=88 amount=1 +kerning first=79 second=46 amount=-4 +kerning first=76 second=89 amount=-8 +kerning first=68 second=65 amount=-1 +kerning first=79 second=84 amount=-1 +kerning first=87 second=100 amount=-1 +kerning first=75 second=103 amount=-1 +kerning first=90 second=67 amount=-1 +kerning first=69 second=103 amount=-1 +kerning first=90 second=71 amount=-1 +kerning first=86 second=44 amount=-8 +kerning first=69 second=121 amount=-1 +kerning first=87 second=114 amount=-1 +kerning first=118 second=39 amount=1 +kerning first=46 second=39 amount=-6 +kerning first=72 second=84 amount=-1 +kerning first=86 second=46 amount=-8 +kerning first=69 second=113 amount=-1 +kerning first=69 second=119 amount=-1 +kerning first=39 second=39 amount=-4 +kerning first=69 second=117 amount=-1 +kerning first=111 second=39 amount=-5 +kerning first=90 second=81 amount=-1 diff --git a/src/web/static/fonts/bmfonts/Roboto72White.png b/src/web/static/fonts/bmfonts/Roboto72White.png new file mode 100644 index 00000000..423a3a7e Binary files /dev/null and b/src/web/static/fonts/bmfonts/Roboto72White.png differ diff --git a/src/web/static/fonts/bmfonts/RobotoBlack72White.fnt b/src/web/static/fonts/bmfonts/RobotoBlack72White.fnt new file mode 100644 index 00000000..d82c9c7b --- /dev/null +++ b/src/web/static/fonts/bmfonts/RobotoBlack72White.fnt @@ -0,0 +1,488 @@ +info face="Roboto Black" size=72 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2 +common lineHeight=85 base=67 scaleW=512 scaleH=512 pages=1 packed=0 +page id=0 file="RobotoBlack72White.png" +chars count=98 +char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=0 page=0 chnl=0 +char id=10 x=0 y=0 width=70 height=99 xoffset=2 yoffset=-11 xadvance=74 page=0 chnl=0 +char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=66 xadvance=18 page=0 chnl=0 +char id=33 x=460 y=156 width=15 height=55 xoffset=3 yoffset=14 xadvance=20 page=0 chnl=0 +char id=34 x=207 y=362 width=22 height=22 xoffset=0 yoffset=12 xadvance=23 page=0 chnl=0 +char id=35 x=404 y=266 width=41 height=54 xoffset=0 yoffset=14 xadvance=42 page=0 chnl=0 +char id=36 x=220 y=0 width=38 height=69 xoffset=2 yoffset=7 xadvance=42 page=0 chnl=0 +char id=37 x=167 y=156 width=49 height=56 xoffset=2 yoffset=13 xadvance=53 page=0 chnl=0 +char id=38 x=216 y=156 width=48 height=56 xoffset=1 yoffset=13 xadvance=48 page=0 chnl=0 +char id=39 x=499 y=320 width=10 height=22 xoffset=1 yoffset=12 xadvance=11 page=0 chnl=0 +char id=40 x=70 y=0 width=22 height=75 xoffset=3 yoffset=9 xadvance=25 page=0 chnl=0 +char id=41 x=92 y=0 width=23 height=75 xoffset=0 yoffset=9 xadvance=25 page=0 chnl=0 +char id=42 x=103 y=362 width=36 height=34 xoffset=-1 yoffset=14 xadvance=33 page=0 chnl=0 +char id=43 x=0 y=362 width=37 height=40 xoffset=1 yoffset=23 xadvance=39 page=0 chnl=0 +char id=44 x=483 y=320 width=16 height=25 xoffset=0 yoffset=57 xadvance=20 page=0 chnl=0 +char id=45 x=308 y=362 width=23 height=12 xoffset=4 yoffset=38 xadvance=32 page=0 chnl=0 +char id=46 x=270 y=362 width=15 height=15 xoffset=3 yoffset=54 xadvance=22 page=0 chnl=0 +char id=47 x=374 y=0 width=29 height=58 xoffset=-3 yoffset=14 xadvance=25 page=0 chnl=0 +char id=48 x=77 y=156 width=38 height=56 xoffset=2 yoffset=13 xadvance=42 page=0 chnl=0 +char id=49 x=299 y=266 width=26 height=54 xoffset=4 yoffset=14 xadvance=41 page=0 chnl=0 +char id=50 x=383 y=156 width=39 height=55 xoffset=1 yoffset=13 xadvance=42 page=0 chnl=0 +char id=51 x=434 y=99 width=39 height=56 xoffset=1 yoffset=13 xadvance=42 page=0 chnl=0 +char id=52 x=325 y=266 width=40 height=54 xoffset=1 yoffset=14 xadvance=42 page=0 chnl=0 +char id=53 x=422 y=156 width=38 height=55 xoffset=2 yoffset=14 xadvance=42 page=0 chnl=0 +char id=54 x=0 y=156 width=39 height=56 xoffset=2 yoffset=13 xadvance=42 page=0 chnl=0 +char id=55 x=365 y=266 width=39 height=54 xoffset=1 yoffset=14 xadvance=42 page=0 chnl=0 +char id=56 x=473 y=99 width=38 height=56 xoffset=2 yoffset=13 xadvance=42 page=0 chnl=0 +char id=57 x=39 y=156 width=38 height=56 xoffset=2 yoffset=13 xadvance=42 page=0 chnl=0 +char id=58 x=471 y=266 width=15 height=43 xoffset=3 yoffset=26 xadvance=21 page=0 chnl=0 +char id=59 x=150 y=156 width=17 height=56 xoffset=1 yoffset=26 xadvance=21 page=0 chnl=0 +char id=60 x=37 y=362 width=33 height=38 xoffset=1 yoffset=26 xadvance=37 page=0 chnl=0 +char id=61 x=172 y=362 width=35 height=27 xoffset=3 yoffset=31 xadvance=42 page=0 chnl=0 +char id=62 x=70 y=362 width=33 height=38 xoffset=3 yoffset=26 xadvance=37 page=0 chnl=0 +char id=63 x=115 y=156 width=35 height=56 xoffset=0 yoffset=13 xadvance=36 page=0 chnl=0 +char id=64 x=258 y=0 width=61 height=68 xoffset=1 yoffset=16 xadvance=64 page=0 chnl=0 +char id=65 x=0 y=212 width=53 height=54 xoffset=-2 yoffset=14 xadvance=49 page=0 chnl=0 +char id=66 x=53 y=212 width=42 height=54 xoffset=3 yoffset=14 xadvance=47 page=0 chnl=0 +char id=67 x=37 y=99 width=46 height=56 xoffset=1 yoffset=13 xadvance=47 page=0 chnl=0 +char id=68 x=95 y=212 width=42 height=54 xoffset=3 yoffset=14 xadvance=47 page=0 chnl=0 +char id=69 x=137 y=212 width=38 height=54 xoffset=3 yoffset=14 xadvance=41 page=0 chnl=0 +char id=70 x=475 y=156 width=36 height=54 xoffset=3 yoffset=14 xadvance=39 page=0 chnl=0 +char id=71 x=83 y=99 width=45 height=56 xoffset=2 yoffset=13 xadvance=49 page=0 chnl=0 +char id=72 x=175 y=212 width=45 height=54 xoffset=3 yoffset=14 xadvance=51 page=0 chnl=0 +char id=73 x=220 y=212 width=14 height=54 xoffset=4 yoffset=14 xadvance=22 page=0 chnl=0 +char id=74 x=264 y=156 width=37 height=55 xoffset=0 yoffset=14 xadvance=40 page=0 chnl=0 +char id=75 x=234 y=212 width=45 height=54 xoffset=3 yoffset=14 xadvance=46 page=0 chnl=0 +char id=76 x=279 y=212 width=36 height=54 xoffset=3 yoffset=14 xadvance=39 page=0 chnl=0 +char id=77 x=315 y=212 width=58 height=54 xoffset=3 yoffset=14 xadvance=63 page=0 chnl=0 +char id=78 x=373 y=212 width=45 height=54 xoffset=3 yoffset=14 xadvance=51 page=0 chnl=0 +char id=79 x=128 y=99 width=47 height=56 xoffset=1 yoffset=13 xadvance=50 page=0 chnl=0 +char id=80 x=418 y=212 width=43 height=54 xoffset=3 yoffset=14 xadvance=48 page=0 chnl=0 +char id=81 x=319 y=0 width=47 height=65 xoffset=2 yoffset=13 xadvance=50 page=0 chnl=0 +char id=82 x=461 y=212 width=43 height=54 xoffset=3 yoffset=14 xadvance=46 page=0 chnl=0 +char id=83 x=175 y=99 width=42 height=56 xoffset=1 yoffset=13 xadvance=44 page=0 chnl=0 +char id=84 x=0 y=266 width=45 height=54 xoffset=0 yoffset=14 xadvance=45 page=0 chnl=0 +char id=85 x=301 y=156 width=42 height=55 xoffset=3 yoffset=14 xadvance=48 page=0 chnl=0 +char id=86 x=45 y=266 width=51 height=54 xoffset=-2 yoffset=14 xadvance=48 page=0 chnl=0 +char id=87 x=96 y=266 width=64 height=54 xoffset=-1 yoffset=14 xadvance=63 page=0 chnl=0 +char id=88 x=160 y=266 width=48 height=54 xoffset=-1 yoffset=14 xadvance=46 page=0 chnl=0 +char id=89 x=208 y=266 width=49 height=54 xoffset=-2 yoffset=14 xadvance=45 page=0 chnl=0 +char id=90 x=257 y=266 width=42 height=54 xoffset=1 yoffset=14 xadvance=44 page=0 chnl=0 +char id=91 x=115 y=0 width=18 height=75 xoffset=3 yoffset=5 xadvance=21 page=0 chnl=0 +char id=92 x=403 y=0 width=37 height=58 xoffset=-2 yoffset=14 xadvance=31 page=0 chnl=0 +char id=93 x=133 y=0 width=18 height=75 xoffset=0 yoffset=5 xadvance=21 page=0 chnl=0 +char id=94 x=139 y=362 width=33 height=28 xoffset=0 yoffset=14 xadvance=32 page=0 chnl=0 +char id=95 x=331 y=362 width=34 height=12 xoffset=-1 yoffset=65 xadvance=33 page=0 chnl=0 +char id=96 x=285 y=362 width=23 height=13 xoffset=0 yoffset=12 xadvance=24 page=0 chnl=0 +char id=97 x=0 y=320 width=37 height=42 xoffset=1 yoffset=27 xadvance=38 page=0 chnl=0 +char id=98 x=440 y=0 width=37 height=57 xoffset=2 yoffset=12 xadvance=40 page=0 chnl=0 +char id=99 x=37 y=320 width=36 height=42 xoffset=1 yoffset=27 xadvance=38 page=0 chnl=0 +char id=100 x=0 y=99 width=37 height=57 xoffset=1 yoffset=12 xadvance=40 page=0 chnl=0 +char id=101 x=73 y=320 width=38 height=42 xoffset=1 yoffset=27 xadvance=39 page=0 chnl=0 +char id=102 x=477 y=0 width=28 height=57 xoffset=0 yoffset=11 xadvance=27 page=0 chnl=0 +char id=103 x=217 y=99 width=38 height=56 xoffset=1 yoffset=27 xadvance=41 page=0 chnl=0 +char id=104 x=255 y=99 width=36 height=56 xoffset=2 yoffset=12 xadvance=40 page=0 chnl=0 +char id=105 x=291 y=99 width=15 height=56 xoffset=2 yoffset=12 xadvance=19 page=0 chnl=0 +char id=106 x=197 y=0 width=23 height=71 xoffset=-5 yoffset=12 xadvance=20 page=0 chnl=0 +char id=107 x=306 y=99 width=40 height=56 xoffset=2 yoffset=12 xadvance=39 page=0 chnl=0 +char id=108 x=346 y=99 width=14 height=56 xoffset=3 yoffset=12 xadvance=20 page=0 chnl=0 +char id=109 x=186 y=320 width=58 height=41 xoffset=2 yoffset=27 xadvance=63 page=0 chnl=0 +char id=110 x=244 y=320 width=36 height=41 xoffset=2 yoffset=27 xadvance=40 page=0 chnl=0 +char id=111 x=111 y=320 width=39 height=42 xoffset=1 yoffset=27 xadvance=41 page=0 chnl=0 +char id=112 x=360 y=99 width=37 height=56 xoffset=2 yoffset=27 xadvance=40 page=0 chnl=0 +char id=113 x=397 y=99 width=37 height=56 xoffset=1 yoffset=27 xadvance=40 page=0 chnl=0 +char id=114 x=486 y=266 width=25 height=41 xoffset=2 yoffset=27 xadvance=27 page=0 chnl=0 +char id=115 x=150 y=320 width=36 height=42 xoffset=0 yoffset=27 xadvance=37 page=0 chnl=0 +char id=116 x=445 y=266 width=26 height=51 xoffset=0 yoffset=18 xadvance=25 page=0 chnl=0 +char id=117 x=280 y=320 width=36 height=41 xoffset=2 yoffset=28 xadvance=40 page=0 chnl=0 +char id=118 x=316 y=320 width=39 height=40 xoffset=-1 yoffset=28 xadvance=37 page=0 chnl=0 +char id=119 x=355 y=320 width=54 height=40 xoffset=-1 yoffset=28 xadvance=52 page=0 chnl=0 +char id=120 x=409 y=320 width=40 height=40 xoffset=-1 yoffset=28 xadvance=37 page=0 chnl=0 +char id=121 x=343 y=156 width=40 height=55 xoffset=-1 yoffset=28 xadvance=37 page=0 chnl=0 +char id=122 x=449 y=320 width=34 height=40 xoffset=1 yoffset=28 xadvance=36 page=0 chnl=0 +char id=123 x=151 y=0 width=23 height=72 xoffset=0 yoffset=9 xadvance=23 page=0 chnl=0 +char id=124 x=366 y=0 width=8 height=63 xoffset=5 yoffset=14 xadvance=18 page=0 chnl=0 +char id=125 x=174 y=0 width=23 height=72 xoffset=0 yoffset=9 xadvance=23 page=0 chnl=0 +char id=126 x=229 y=362 width=41 height=19 xoffset=2 yoffset=36 xadvance=45 page=0 chnl=0 +char id=127 x=0 y=0 width=70 height=99 xoffset=2 yoffset=-11 xadvance=74 page=0 chnl=0 +kernings count=385 +kerning first=84 second=74 amount=-8 +kerning first=86 second=100 amount=-2 +kerning first=114 second=113 amount=-1 +kerning first=70 second=121 amount=-1 +kerning first=34 second=99 amount=-2 +kerning first=70 second=99 amount=-1 +kerning first=69 second=99 amount=-1 +kerning first=88 second=113 amount=-1 +kerning first=84 second=46 amount=-9 +kerning first=87 second=97 amount=-1 +kerning first=90 second=117 amount=-1 +kerning first=39 second=97 amount=-2 +kerning first=69 second=111 amount=-1 +kerning first=87 second=41 amount=1 +kerning first=121 second=34 amount=1 +kerning first=40 second=86 amount=1 +kerning first=85 second=65 amount=-1 +kerning first=72 second=65 amount=1 +kerning first=114 second=102 amount=1 +kerning first=89 second=42 amount=-2 +kerning first=114 second=34 amount=1 +kerning first=75 second=67 amount=-1 +kerning first=89 second=85 amount=-3 +kerning first=77 second=88 amount=1 +kerning first=84 second=115 amount=-3 +kerning first=84 second=71 amount=-1 +kerning first=89 second=101 amount=-2 +kerning first=89 second=45 amount=-5 +kerning first=78 second=88 amount=1 +kerning first=68 second=89 amount=-2 +kerning first=122 second=103 amount=-1 +kerning first=78 second=84 amount=-1 +kerning first=86 second=103 amount=-2 +kerning first=89 second=79 amount=-1 +kerning first=75 second=111 amount=-1 +kerning first=111 second=120 amount=-1 +kerning first=87 second=44 amount=-5 +kerning first=67 second=84 amount=-1 +kerning first=84 second=111 amount=-7 +kerning first=84 second=83 amount=-1 +kerning first=102 second=113 amount=-1 +kerning first=39 second=101 amount=-2 +kerning first=80 second=88 amount=-2 +kerning first=66 second=84 amount=-1 +kerning first=65 second=87 amount=-1 +kerning first=122 second=100 amount=-1 +kerning first=75 second=118 amount=-1 +kerning first=73 second=65 amount=1 +kerning first=70 second=118 amount=-1 +kerning first=73 second=88 amount=1 +kerning first=82 second=89 amount=-2 +kerning first=65 second=34 amount=-4 +kerning first=120 second=99 amount=-1 +kerning first=84 second=99 amount=-3 +kerning first=84 second=65 amount=-4 +kerning first=112 second=39 amount=-1 +kerning first=76 second=39 amount=-10 +kerning first=78 second=65 amount=1 +kerning first=88 second=45 amount=-5 +kerning first=34 second=111 amount=-3 +kerning first=114 second=99 amount=-1 +kerning first=86 second=125 amount=1 +kerning first=70 second=111 amount=-1 +kerning first=89 second=120 amount=-1 +kerning first=90 second=119 amount=-1 +kerning first=89 second=89 amount=1 +kerning first=89 second=117 amount=-1 +kerning first=75 second=117 amount=-1 +kerning first=76 second=65 amount=1 +kerning first=34 second=34 amount=-1 +kerning first=89 second=110 amount=-1 +kerning first=88 second=101 amount=-1 +kerning first=107 second=103 amount=-1 +kerning first=34 second=115 amount=-3 +kerning first=80 second=44 amount=-14 +kerning first=98 second=39 amount=-1 +kerning first=70 second=65 amount=-7 +kerning first=89 second=116 amount=-1 +kerning first=70 second=46 amount=-10 +kerning first=98 second=34 amount=-1 +kerning first=70 second=84 amount=1 +kerning first=114 second=100 amount=-1 +kerning first=88 second=79 amount=-1 +kerning first=39 second=113 amount=-2 +kerning first=65 second=118 amount=-2 +kerning first=114 second=103 amount=-1 +kerning first=77 second=65 amount=1 +kerning first=120 second=103 amount=-1 +kerning first=65 second=110 amount=-2 +kerning first=114 second=121 amount=1 +kerning first=89 second=100 amount=-2 +kerning first=80 second=65 amount=-6 +kerning first=121 second=111 amount=-1 +kerning first=34 second=101 amount=-2 +kerning first=122 second=111 amount=-1 +kerning first=114 second=118 amount=1 +kerning first=102 second=41 amount=1 +kerning first=122 second=113 amount=-1 +kerning first=89 second=122 amount=-1 +kerning first=68 second=88 amount=-1 +kerning first=81 second=89 amount=-1 +kerning first=114 second=111 amount=-1 +kerning first=46 second=34 amount=-10 +kerning first=84 second=112 amount=-3 +kerning first=76 second=34 amount=-10 +kerning first=39 second=115 amount=-3 +kerning first=76 second=118 amount=-4 +kerning first=86 second=99 amount=-2 +kerning first=84 second=84 amount=1 +kerning first=120 second=111 amount=-1 +kerning first=65 second=79 amount=-1 +kerning first=87 second=101 amount=-1 +kerning first=67 second=125 amount=-1 +kerning first=120 second=113 amount=-1 +kerning first=118 second=46 amount=-6 +kerning first=88 second=103 amount=-1 +kerning first=111 second=122 amount=-1 +kerning first=77 second=84 amount=-1 +kerning first=114 second=46 amount=-6 +kerning first=34 second=39 amount=-1 +kerning first=65 second=121 amount=-2 +kerning first=114 second=44 amount=-6 +kerning first=69 second=84 amount=1 +kerning first=89 second=46 amount=-8 +kerning first=97 second=39 amount=-1 +kerning first=34 second=100 amount=-2 +kerning first=70 second=100 amount=-1 +kerning first=84 second=120 amount=-3 +kerning first=90 second=118 amount=-1 +kerning first=70 second=114 amount=-1 +kerning first=34 second=112 amount=-1 +kerning first=89 second=86 amount=1 +kerning first=86 second=113 amount=-2 +kerning first=88 second=71 amount=-1 +kerning first=122 second=99 amount=-1 +kerning first=66 second=89 amount=-2 +kerning first=102 second=103 amount=-1 +kerning first=88 second=67 amount=-1 +kerning first=39 second=110 amount=-1 +kerning first=88 second=117 amount=-1 +kerning first=89 second=118 amount=-1 +kerning first=97 second=118 amount=-1 +kerning first=87 second=65 amount=-2 +kerning first=89 second=67 amount=-1 +kerning first=89 second=74 amount=-3 +kerning first=102 second=101 amount=-1 +kerning first=86 second=111 amount=-2 +kerning first=65 second=119 amount=-1 +kerning first=84 second=100 amount=-3 +kerning first=120 second=100 amount=-1 +kerning first=104 second=34 amount=-3 +kerning first=86 second=41 amount=1 +kerning first=111 second=34 amount=-3 +kerning first=40 second=89 amount=1 +kerning first=121 second=39 amount=1 +kerning first=70 second=74 amount=-7 +kerning first=68 second=90 amount=-1 +kerning first=98 second=120 amount=-1 +kerning first=110 second=34 amount=-3 +kerning first=119 second=46 amount=-4 +kerning first=69 second=102 amount=-1 +kerning first=118 second=44 amount=-6 +kerning first=84 second=114 amount=-2 +kerning first=86 second=97 amount=-2 +kerning first=40 second=87 amount=1 +kerning first=65 second=109 amount=-2 +kerning first=68 second=86 amount=-1 +kerning first=86 second=93 amount=1 +kerning first=65 second=67 amount=-1 +kerning first=97 second=34 amount=-1 +kerning first=34 second=65 amount=-4 +kerning first=84 second=118 amount=-3 +kerning first=112 second=34 amount=-1 +kerning first=76 second=84 amount=-7 +kerning first=107 second=99 amount=-1 +kerning first=123 second=85 amount=-1 +kerning first=102 second=125 amount=1 +kerning first=65 second=63 amount=-3 +kerning first=89 second=44 amount=-8 +kerning first=80 second=118 amount=1 +kerning first=112 second=122 amount=-1 +kerning first=79 second=65 amount=-1 +kerning first=80 second=121 amount=1 +kerning first=118 second=34 amount=1 +kerning first=87 second=45 amount=-2 +kerning first=69 second=100 amount=-1 +kerning first=87 second=103 amount=-1 +kerning first=112 second=120 amount=-1 +kerning first=86 second=65 amount=-3 +kerning first=65 second=81 amount=-1 +kerning first=68 second=44 amount=-4 +kerning first=86 second=45 amount=-6 +kerning first=39 second=34 amount=-1 +kerning first=72 second=88 amount=1 +kerning first=68 second=46 amount=-4 +kerning first=65 second=89 amount=-5 +kerning first=69 second=118 amount=-1 +kerning first=89 second=38 amount=-1 +kerning first=88 second=99 amount=-1 +kerning first=65 second=71 amount=-1 +kerning first=91 second=74 amount=-1 +kerning first=75 second=101 amount=-1 +kerning first=39 second=112 amount=-1 +kerning first=70 second=113 amount=-1 +kerning first=119 second=44 amount=-4 +kerning first=72 second=89 amount=-1 +kerning first=90 second=103 amount=-1 +kerning first=65 second=86 amount=-3 +kerning first=84 second=119 amount=-2 +kerning first=34 second=110 amount=-1 +kerning first=39 second=109 amount=-1 +kerning first=75 second=81 amount=-1 +kerning first=89 second=115 amount=-2 +kerning first=89 second=87 amount=1 +kerning first=114 second=101 amount=-1 +kerning first=116 second=111 amount=-1 +kerning first=90 second=100 amount=-1 +kerning first=79 second=89 amount=-2 +kerning first=84 second=122 amount=-2 +kerning first=68 second=84 amount=-3 +kerning first=76 second=86 amount=-7 +kerning first=74 second=65 amount=-1 +kerning first=107 second=101 amount=-1 +kerning first=80 second=46 amount=-14 +kerning first=89 second=93 amount=1 +kerning first=89 second=65 amount=-5 +kerning first=87 second=117 amount=-1 +kerning first=89 second=81 amount=-1 +kerning first=39 second=103 amount=-2 +kerning first=86 second=101 amount=-2 +kerning first=86 second=117 amount=-1 +kerning first=84 second=113 amount=-3 +kerning first=87 second=46 amount=-5 +kerning first=47 second=47 amount=-9 +kerning first=75 second=103 amount=-1 +kerning first=89 second=84 amount=1 +kerning first=84 second=110 amount=-3 +kerning first=39 second=99 amount=-2 +kerning first=88 second=121 amount=-1 +kerning first=65 second=39 amount=-4 +kerning first=110 second=39 amount=-3 +kerning first=88 second=118 amount=-1 +kerning first=86 second=114 amount=-1 +kerning first=80 second=74 amount=-6 +kerning first=84 second=97 amount=-6 +kerning first=82 second=84 amount=-2 +kerning first=91 second=85 amount=-1 +kerning first=102 second=99 amount=-1 +kerning first=66 second=86 amount=-1 +kerning first=120 second=101 amount=-1 +kerning first=102 second=93 amount=1 +kerning first=75 second=100 amount=-1 +kerning first=84 second=79 amount=-1 +kerning first=44 second=39 amount=-10 +kerning first=111 second=121 amount=-1 +kerning first=75 second=121 amount=-1 +kerning first=81 second=87 amount=-1 +kerning first=107 second=113 amount=-1 +kerning first=90 second=79 amount=-1 +kerning first=89 second=114 amount=-1 +kerning first=122 second=101 amount=-1 +kerning first=111 second=118 amount=-1 +kerning first=82 second=86 amount=-1 +kerning first=70 second=101 amount=-1 +kerning first=114 second=97 amount=-1 +kerning first=70 second=97 amount=-1 +kerning first=34 second=97 amount=-2 +kerning first=89 second=102 amount=-1 +kerning first=78 second=89 amount=-1 +kerning first=70 second=44 amount=-10 +kerning first=104 second=39 amount=-3 +kerning first=84 second=45 amount=-10 +kerning first=89 second=121 amount=-1 +kerning first=109 second=34 amount=-3 +kerning first=84 second=86 amount=1 +kerning first=87 second=99 amount=-1 +kerning first=32 second=84 amount=-2 +kerning first=98 second=122 amount=-1 +kerning first=89 second=112 amount=-1 +kerning first=89 second=103 amount=-2 +kerning first=65 second=116 amount=-1 +kerning first=88 second=81 amount=-1 +kerning first=102 second=34 amount=1 +kerning first=109 second=39 amount=-3 +kerning first=81 second=84 amount=-1 +kerning first=121 second=97 amount=-1 +kerning first=89 second=99 amount=-2 +kerning first=89 second=125 amount=1 +kerning first=81 second=86 amount=-1 +kerning first=114 second=116 amount=2 +kerning first=114 second=119 amount=1 +kerning first=84 second=44 amount=-9 +kerning first=102 second=39 amount=1 +kerning first=44 second=34 amount=-10 +kerning first=34 second=109 amount=-1 +kerning first=84 second=101 amount=-3 +kerning first=75 second=119 amount=-2 +kerning first=84 second=81 amount=-1 +kerning first=76 second=121 amount=-4 +kerning first=69 second=101 amount=-1 +kerning first=80 second=90 amount=-1 +kerning first=89 second=97 amount=-2 +kerning first=89 second=109 amount=-1 +kerning first=90 second=99 amount=-1 +kerning first=79 second=88 amount=-1 +kerning first=70 second=103 amount=-1 +kerning first=34 second=103 amount=-2 +kerning first=84 second=67 amount=-1 +kerning first=76 second=79 amount=-2 +kerning first=34 second=113 amount=-2 +kerning first=89 second=41 amount=1 +kerning first=75 second=71 amount=-1 +kerning first=76 second=87 amount=-3 +kerning first=77 second=89 amount=-1 +kerning first=90 second=113 amount=-1 +kerning first=118 second=111 amount=-1 +kerning first=118 second=97 amount=-1 +kerning first=88 second=100 amount=-1 +kerning first=89 second=111 amount=-2 +kerning first=90 second=121 amount=-1 +kerning first=89 second=113 amount=-2 +kerning first=84 second=87 amount=1 +kerning first=39 second=111 amount=-3 +kerning first=39 second=100 amount=-2 +kerning first=75 second=113 amount=-1 +kerning first=88 second=111 amount=-1 +kerning first=87 second=111 amount=-1 +kerning first=89 second=83 amount=-1 +kerning first=84 second=89 amount=1 +kerning first=84 second=103 amount=-3 +kerning first=70 second=117 amount=-1 +kerning first=67 second=41 amount=-1 +kerning first=89 second=71 amount=-1 +kerning first=121 second=44 amount=-6 +kerning first=97 second=121 amount=-1 +kerning first=87 second=113 amount=-1 +kerning first=73 second=84 amount=-1 +kerning first=121 second=46 amount=-6 +kerning first=75 second=99 amount=-1 +kerning first=65 second=112 amount=-2 +kerning first=65 second=85 amount=-1 +kerning first=76 second=67 amount=-2 +kerning first=76 second=81 amount=-2 +kerning first=102 second=100 amount=-1 +kerning first=75 second=79 amount=-1 +kerning first=39 second=65 amount=-4 +kerning first=65 second=84 amount=-4 +kerning first=90 second=101 amount=-1 +kerning first=84 second=121 amount=-3 +kerning first=114 second=39 amount=1 +kerning first=84 second=109 amount=-3 +kerning first=123 second=74 amount=-1 +kerning first=76 second=119 amount=-2 +kerning first=84 second=117 amount=-2 +kerning first=76 second=85 amount=-1 +kerning first=76 second=71 amount=-2 +kerning first=79 second=90 amount=-1 +kerning first=107 second=100 amount=-1 +kerning first=90 second=111 amount=-1 +kerning first=79 second=44 amount=-4 +kerning first=75 second=45 amount=-6 +kerning first=79 second=86 amount=-1 +kerning first=79 second=46 amount=-4 +kerning first=76 second=89 amount=-10 +kerning first=68 second=65 amount=-1 +kerning first=79 second=84 amount=-3 +kerning first=87 second=100 amount=-1 +kerning first=84 second=32 amount=-2 +kerning first=90 second=67 amount=-1 +kerning first=69 second=103 amount=-1 +kerning first=90 second=71 amount=-1 +kerning first=86 second=44 amount=-8 +kerning first=69 second=121 amount=-1 +kerning first=87 second=114 amount=-1 +kerning first=118 second=39 amount=1 +kerning first=46 second=39 amount=-10 +kerning first=72 second=84 amount=-1 +kerning first=86 second=46 amount=-8 +kerning first=69 second=113 amount=-1 +kerning first=69 second=119 amount=-1 +kerning first=73 second=89 amount=-1 +kerning first=39 second=39 amount=-1 +kerning first=69 second=117 amount=-1 +kerning first=111 second=39 amount=-3 +kerning first=90 second=81 amount=-1 diff --git a/src/web/static/fonts/bmfonts/RobotoBlack72White.png b/src/web/static/fonts/bmfonts/RobotoBlack72White.png new file mode 100644 index 00000000..f3bbba30 Binary files /dev/null and b/src/web/static/fonts/bmfonts/RobotoBlack72White.png differ diff --git a/src/web/static/fonts/bmfonts/RobotoMono72White.fnt b/src/web/static/fonts/bmfonts/RobotoMono72White.fnt new file mode 100644 index 00000000..b9be89b4 --- /dev/null +++ b/src/web/static/fonts/bmfonts/RobotoMono72White.fnt @@ -0,0 +1,103 @@ +info face="Roboto Mono" size=72 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2 +common lineHeight=96 base=76 scaleW=512 scaleH=512 pages=1 packed=0 +page id=0 file="RobotoMono72White.png" +chars count=98 +char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=0 page=0 chnl=0 +char id=10 x=0 y=0 width=45 height=99 xoffset=-1 yoffset=-2 xadvance=43 page=0 chnl=0 +char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=43 page=0 chnl=0 +char id=33 x=498 y=99 width=10 height=55 xoffset=16 yoffset=23 xadvance=43 page=0 chnl=0 +char id=34 x=434 y=319 width=20 height=19 xoffset=11 yoffset=21 xadvance=43 page=0 chnl=0 +char id=35 x=175 y=265 width=41 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0 +char id=36 x=200 y=0 width=35 height=69 xoffset=5 yoffset=15 xadvance=43 page=0 chnl=0 +char id=37 x=0 y=155 width=42 height=56 xoffset=1 yoffset=22 xadvance=44 page=0 chnl=0 +char id=38 x=42 y=155 width=41 height=56 xoffset=3 yoffset=22 xadvance=44 page=0 chnl=0 +char id=39 x=502 y=211 width=7 height=19 xoffset=16 yoffset=21 xadvance=43 page=0 chnl=0 +char id=40 x=45 y=0 width=21 height=78 xoffset=12 yoffset=16 xadvance=44 page=0 chnl=0 +char id=41 x=66 y=0 width=22 height=78 xoffset=9 yoffset=16 xadvance=43 page=0 chnl=0 +char id=42 x=256 y=319 width=37 height=37 xoffset=4 yoffset=32 xadvance=43 page=0 chnl=0 +char id=43 x=219 y=319 width=37 height=40 xoffset=3 yoffset=32 xadvance=43 page=0 chnl=0 +char id=44 x=421 y=319 width=13 height=22 xoffset=11 yoffset=67 xadvance=43 page=0 chnl=0 +char id=45 x=17 y=360 width=29 height=8 xoffset=7 yoffset=49 xadvance=44 page=0 chnl=0 +char id=46 x=496 y=319 width=12 height=13 xoffset=16 yoffset=65 xadvance=43 page=0 chnl=0 +char id=47 x=319 y=0 width=31 height=58 xoffset=7 yoffset=23 xadvance=43 page=0 chnl=0 +char id=48 x=431 y=99 width=35 height=56 xoffset=4 yoffset=22 xadvance=43 page=0 chnl=0 +char id=49 x=36 y=265 width=23 height=54 xoffset=6 yoffset=23 xadvance=44 page=0 chnl=0 +char id=50 x=189 y=155 width=37 height=55 xoffset=2 yoffset=22 xadvance=44 page=0 chnl=0 +char id=51 x=361 y=99 width=35 height=56 xoffset=2 yoffset=22 xadvance=43 page=0 chnl=0 +char id=52 x=59 y=265 width=39 height=54 xoffset=2 yoffset=23 xadvance=44 page=0 chnl=0 +char id=53 x=226 y=155 width=35 height=55 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0 +char id=54 x=261 y=155 width=35 height=55 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0 +char id=55 x=98 y=265 width=37 height=54 xoffset=3 yoffset=23 xadvance=44 page=0 chnl=0 +char id=56 x=396 y=99 width=35 height=56 xoffset=5 yoffset=22 xadvance=43 page=0 chnl=0 +char id=57 x=296 y=155 width=34 height=55 xoffset=4 yoffset=22 xadvance=43 page=0 chnl=0 +char id=58 x=490 y=211 width=12 height=43 xoffset=18 yoffset=35 xadvance=43 page=0 chnl=0 +char id=59 x=486 y=0 width=14 height=55 xoffset=16 yoffset=35 xadvance=43 page=0 chnl=0 +char id=60 x=293 y=319 width=32 height=35 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0 +char id=61 x=388 y=319 width=33 height=23 xoffset=5 yoffset=41 xadvance=43 page=0 chnl=0 +char id=62 x=325 y=319 width=33 height=35 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0 +char id=63 x=466 y=99 width=32 height=56 xoffset=6 yoffset=22 xadvance=43 page=0 chnl=0 +char id=64 x=135 y=265 width=40 height=54 xoffset=1 yoffset=23 xadvance=42 page=0 chnl=0 +char id=65 x=330 y=155 width=42 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0 +char id=66 x=372 y=155 width=35 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0 +char id=67 x=448 y=0 width=38 height=56 xoffset=3 yoffset=22 xadvance=43 page=0 chnl=0 +char id=68 x=407 y=155 width=37 height=54 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0 +char id=69 x=444 y=155 width=34 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0 +char id=70 x=0 y=211 width=34 height=54 xoffset=6 yoffset=23 xadvance=44 page=0 chnl=0 +char id=71 x=0 y=99 width=38 height=56 xoffset=3 yoffset=22 xadvance=44 page=0 chnl=0 +char id=72 x=34 y=211 width=36 height=54 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0 +char id=73 x=478 y=155 width=33 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0 +char id=74 x=83 y=155 width=36 height=55 xoffset=2 yoffset=23 xadvance=43 page=0 chnl=0 +char id=75 x=70 y=211 width=38 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0 +char id=76 x=108 y=211 width=34 height=54 xoffset=6 yoffset=23 xadvance=43 page=0 chnl=0 +char id=77 x=142 y=211 width=36 height=54 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0 +char id=78 x=178 y=211 width=35 height=54 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0 +char id=79 x=38 y=99 width=38 height=56 xoffset=3 yoffset=22 xadvance=43 page=0 chnl=0 +char id=80 x=213 y=211 width=36 height=54 xoffset=6 yoffset=23 xadvance=43 page=0 chnl=0 +char id=81 x=242 y=0 width=40 height=64 xoffset=2 yoffset=22 xadvance=43 page=0 chnl=0 +char id=82 x=249 y=211 width=36 height=54 xoffset=5 yoffset=23 xadvance=43 page=0 chnl=0 +char id=83 x=76 y=99 width=38 height=56 xoffset=3 yoffset=22 xadvance=44 page=0 chnl=0 +char id=84 x=285 y=211 width=40 height=54 xoffset=2 yoffset=23 xadvance=44 page=0 chnl=0 +char id=85 x=119 y=155 width=36 height=55 xoffset=4 yoffset=23 xadvance=43 page=0 chnl=0 +char id=86 x=325 y=211 width=41 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0 +char id=87 x=366 y=211 width=42 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0 +char id=88 x=408 y=211 width=41 height=54 xoffset=2 yoffset=23 xadvance=43 page=0 chnl=0 +char id=89 x=449 y=211 width=41 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0 +char id=90 x=0 y=265 width=36 height=54 xoffset=3 yoffset=23 xadvance=43 page=0 chnl=0 +char id=91 x=88 y=0 width=16 height=72 xoffset=14 yoffset=16 xadvance=43 page=0 chnl=0 +char id=92 x=350 y=0 width=30 height=58 xoffset=7 yoffset=23 xadvance=43 page=0 chnl=0 +char id=93 x=104 y=0 width=17 height=72 xoffset=13 yoffset=16 xadvance=44 page=0 chnl=0 +char id=94 x=358 y=319 width=30 height=30 xoffset=7 yoffset=23 xadvance=43 page=0 chnl=0 +char id=95 x=46 y=360 width=34 height=8 xoffset=4 yoffset=74 xadvance=43 page=0 chnl=0 +char id=96 x=0 y=360 width=17 height=12 xoffset=13 yoffset=22 xadvance=43 page=0 chnl=0 +char id=97 x=251 y=265 width=35 height=42 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0 +char id=98 x=380 y=0 width=34 height=57 xoffset=5 yoffset=21 xadvance=43 page=0 chnl=0 +char id=99 x=286 y=265 width=35 height=42 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0 +char id=100 x=414 y=0 width=34 height=57 xoffset=4 yoffset=21 xadvance=43 page=0 chnl=0 +char id=101 x=321 y=265 width=36 height=42 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0 +char id=102 x=282 y=0 width=37 height=58 xoffset=4 yoffset=19 xadvance=43 page=0 chnl=0 +char id=103 x=114 y=99 width=34 height=56 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0 +char id=104 x=148 y=99 width=34 height=56 xoffset=5 yoffset=21 xadvance=43 page=0 chnl=0 +char id=105 x=155 y=155 width=34 height=55 xoffset=6 yoffset=22 xadvance=43 page=0 chnl=0 +char id=106 x=121 y=0 width=26 height=71 xoffset=6 yoffset=22 xadvance=44 page=0 chnl=0 +char id=107 x=182 y=99 width=36 height=56 xoffset=5 yoffset=21 xadvance=43 page=0 chnl=0 +char id=108 x=218 y=99 width=34 height=56 xoffset=6 yoffset=21 xadvance=43 page=0 chnl=0 +char id=109 x=428 y=265 width=39 height=41 xoffset=2 yoffset=36 xadvance=43 page=0 chnl=0 +char id=110 x=467 y=265 width=34 height=41 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0 +char id=111 x=357 y=265 width=37 height=42 xoffset=3 yoffset=36 xadvance=43 page=0 chnl=0 +char id=112 x=252 y=99 width=34 height=56 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0 +char id=113 x=286 y=99 width=34 height=56 xoffset=4 yoffset=36 xadvance=43 page=0 chnl=0 +char id=114 x=0 y=319 width=29 height=41 xoffset=11 yoffset=36 xadvance=44 page=0 chnl=0 +char id=115 x=394 y=265 width=34 height=42 xoffset=5 yoffset=36 xadvance=43 page=0 chnl=0 +char id=116 x=216 y=265 width=35 height=51 xoffset=4 yoffset=27 xadvance=43 page=0 chnl=0 +char id=117 x=29 y=319 width=33 height=41 xoffset=5 yoffset=37 xadvance=43 page=0 chnl=0 +char id=118 x=62 y=319 width=39 height=40 xoffset=2 yoffset=37 xadvance=43 page=0 chnl=0 +char id=119 x=101 y=319 width=43 height=40 xoffset=0 yoffset=37 xadvance=43 page=0 chnl=0 +char id=120 x=144 y=319 width=40 height=40 xoffset=2 yoffset=37 xadvance=43 page=0 chnl=0 +char id=121 x=320 y=99 width=41 height=56 xoffset=1 yoffset=37 xadvance=43 page=0 chnl=0 +char id=122 x=184 y=319 width=35 height=40 xoffset=5 yoffset=37 xadvance=44 page=0 chnl=0 +char id=123 x=147 y=0 width=26 height=71 xoffset=10 yoffset=19 xadvance=43 page=0 chnl=0 +char id=124 x=235 y=0 width=7 height=68 xoffset=18 yoffset=23 xadvance=43 page=0 chnl=0 +char id=125 x=173 y=0 width=27 height=71 xoffset=10 yoffset=19 xadvance=44 page=0 chnl=0 +char id=126 x=454 y=319 width=42 height=16 xoffset=1 yoffset=47 xadvance=44 page=0 chnl=0 +char id=127 x=0 y=0 width=45 height=99 xoffset=-1 yoffset=-2 xadvance=43 page=0 chnl=0 +kernings count=0 diff --git a/src/web/static/fonts/bmfonts/RobotoMono72White.png b/src/web/static/fonts/bmfonts/RobotoMono72White.png new file mode 100644 index 00000000..ed192363 Binary files /dev/null and b/src/web/static/fonts/bmfonts/RobotoMono72White.png differ diff --git a/src/web/static/fonts/bmfonts/RobotoSlab72White.fnt b/src/web/static/fonts/bmfonts/RobotoSlab72White.fnt new file mode 100644 index 00000000..f1926d5c --- /dev/null +++ b/src/web/static/fonts/bmfonts/RobotoSlab72White.fnt @@ -0,0 +1,492 @@ +info face="Roboto Slab Regular" size=72 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2 +common lineHeight=96 base=76 scaleW=512 scaleH=512 pages=1 packed=0 +page id=0 file="RobotoSlab72White.png" +chars count=98 +char id=0 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=0 page=0 chnl=0 +char id=10 x=0 y=0 width=70 height=98 xoffset=0 yoffset=-1 xadvance=70 page=0 chnl=0 +char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=75 xadvance=18 page=0 chnl=0 +char id=33 x=497 y=156 width=9 height=54 xoffset=4 yoffset=23 xadvance=17 page=0 chnl=0 +char id=34 x=191 y=362 width=19 height=20 xoffset=5 yoffset=20 xadvance=28 page=0 chnl=0 +char id=35 x=406 y=266 width=41 height=54 xoffset=1 yoffset=23 xadvance=43 page=0 chnl=0 +char id=36 x=212 y=0 width=35 height=69 xoffset=2 yoffset=15 xadvance=39 page=0 chnl=0 +char id=37 x=174 y=156 width=48 height=56 xoffset=2 yoffset=22 xadvance=52 page=0 chnl=0 +char id=38 x=222 y=156 width=44 height=56 xoffset=2 yoffset=22 xadvance=46 page=0 chnl=0 +char id=39 x=210 y=362 width=8 height=20 xoffset=5 yoffset=20 xadvance=17 page=0 chnl=0 +char id=40 x=70 y=0 width=21 height=77 xoffset=3 yoffset=17 xadvance=23 page=0 chnl=0 +char id=41 x=91 y=0 width=21 height=77 xoffset=-1 yoffset=17 xadvance=23 page=0 chnl=0 +char id=42 x=100 y=362 width=31 height=33 xoffset=1 yoffset=23 xadvance=33 page=0 chnl=0 +char id=43 x=0 y=362 width=37 height=40 xoffset=2 yoffset=32 xadvance=41 page=0 chnl=0 +char id=44 x=492 y=320 width=13 height=21 xoffset=-1 yoffset=67 xadvance=14 page=0 chnl=0 +char id=45 x=287 y=362 width=19 height=8 xoffset=4 yoffset=50 xadvance=27 page=0 chnl=0 +char id=46 x=278 y=362 width=9 height=9 xoffset=4 yoffset=68 xadvance=17 page=0 chnl=0 +char id=47 x=470 y=0 width=30 height=58 xoffset=-1 yoffset=23 xadvance=29 page=0 chnl=0 +char id=48 x=139 y=156 width=35 height=56 xoffset=3 yoffset=22 xadvance=41 page=0 chnl=0 +char id=49 x=305 y=266 width=25 height=54 xoffset=3 yoffset=23 xadvance=30 page=0 chnl=0 +char id=50 x=357 y=156 width=36 height=55 xoffset=2 yoffset=22 xadvance=40 page=0 chnl=0 +char id=51 x=0 y=156 width=34 height=56 xoffset=2 yoffset=22 xadvance=39 page=0 chnl=0 +char id=52 x=330 y=266 width=39 height=54 xoffset=1 yoffset=23 xadvance=42 page=0 chnl=0 +char id=53 x=393 y=156 width=33 height=55 xoffset=2 yoffset=23 xadvance=37 page=0 chnl=0 +char id=54 x=34 y=156 width=35 height=56 xoffset=3 yoffset=22 xadvance=40 page=0 chnl=0 +char id=55 x=369 y=266 width=37 height=54 xoffset=2 yoffset=23 xadvance=40 page=0 chnl=0 +char id=56 x=69 y=156 width=35 height=56 xoffset=2 yoffset=22 xadvance=39 page=0 chnl=0 +char id=57 x=104 y=156 width=35 height=56 xoffset=2 yoffset=22 xadvance=41 page=0 chnl=0 +char id=58 x=500 y=0 width=9 height=40 xoffset=4 yoffset=37 xadvance=15 page=0 chnl=0 +char id=59 x=447 y=266 width=13 height=52 xoffset=0 yoffset=37 xadvance=15 page=0 chnl=0 +char id=60 x=37 y=362 width=31 height=35 xoffset=2 yoffset=39 xadvance=36 page=0 chnl=0 +char id=61 x=160 y=362 width=31 height=23 xoffset=4 yoffset=40 xadvance=39 page=0 chnl=0 +char id=62 x=68 y=362 width=32 height=35 xoffset=3 yoffset=39 xadvance=37 page=0 chnl=0 +char id=63 x=480 y=98 width=31 height=55 xoffset=1 yoffset=22 xadvance=33 page=0 chnl=0 +char id=64 x=247 y=0 width=60 height=68 xoffset=1 yoffset=25 xadvance=64 page=0 chnl=0 +char id=65 x=426 y=156 width=51 height=54 xoffset=1 yoffset=23 xadvance=53 page=0 chnl=0 +char id=66 x=0 y=212 width=44 height=54 xoffset=1 yoffset=23 xadvance=47 page=0 chnl=0 +char id=67 x=191 y=98 width=42 height=56 xoffset=1 yoffset=22 xadvance=46 page=0 chnl=0 +char id=68 x=44 y=212 width=46 height=54 xoffset=1 yoffset=23 xadvance=50 page=0 chnl=0 +char id=69 x=90 y=212 width=42 height=54 xoffset=1 yoffset=23 xadvance=46 page=0 chnl=0 +char id=70 x=132 y=212 width=42 height=54 xoffset=1 yoffset=23 xadvance=44 page=0 chnl=0 +char id=71 x=233 y=98 width=43 height=56 xoffset=1 yoffset=22 xadvance=49 page=0 chnl=0 +char id=72 x=174 y=212 width=52 height=54 xoffset=1 yoffset=23 xadvance=55 page=0 chnl=0 +char id=73 x=477 y=156 width=20 height=54 xoffset=1 yoffset=23 xadvance=22 page=0 chnl=0 +char id=74 x=266 y=156 width=39 height=55 xoffset=1 yoffset=23 xadvance=41 page=0 chnl=0 +char id=75 x=226 y=212 width=48 height=54 xoffset=1 yoffset=23 xadvance=50 page=0 chnl=0 +char id=76 x=274 y=212 width=39 height=54 xoffset=1 yoffset=23 xadvance=42 page=0 chnl=0 +char id=77 x=313 y=212 width=64 height=54 xoffset=1 yoffset=23 xadvance=66 page=0 chnl=0 +char id=78 x=377 y=212 width=52 height=54 xoffset=1 yoffset=23 xadvance=54 page=0 chnl=0 +char id=79 x=276 y=98 width=47 height=56 xoffset=2 yoffset=22 xadvance=51 page=0 chnl=0 +char id=80 x=429 y=212 width=43 height=54 xoffset=1 yoffset=23 xadvance=45 page=0 chnl=0 +char id=81 x=307 y=0 width=48 height=64 xoffset=2 yoffset=22 xadvance=51 page=0 chnl=0 +char id=82 x=0 y=266 width=46 height=54 xoffset=1 yoffset=23 xadvance=48 page=0 chnl=0 +char id=83 x=323 y=98 width=38 height=56 xoffset=3 yoffset=22 xadvance=43 page=0 chnl=0 +char id=84 x=46 y=266 width=45 height=54 xoffset=0 yoffset=23 xadvance=45 page=0 chnl=0 +char id=85 x=305 y=156 width=52 height=55 xoffset=1 yoffset=23 xadvance=54 page=0 chnl=0 +char id=86 x=91 y=266 width=50 height=54 xoffset=1 yoffset=23 xadvance=52 page=0 chnl=0 +char id=87 x=141 y=266 width=67 height=54 xoffset=0 yoffset=23 xadvance=67 page=0 chnl=0 +char id=88 x=208 y=266 width=49 height=54 xoffset=1 yoffset=23 xadvance=51 page=0 chnl=0 +char id=89 x=257 y=266 width=48 height=54 xoffset=1 yoffset=23 xadvance=50 page=0 chnl=0 +char id=90 x=472 y=212 width=38 height=54 xoffset=2 yoffset=23 xadvance=42 page=0 chnl=0 +char id=91 x=180 y=0 width=16 height=72 xoffset=5 yoffset=16 xadvance=21 page=0 chnl=0 +char id=92 x=0 y=98 width=31 height=58 xoffset=0 yoffset=23 xadvance=30 page=0 chnl=0 +char id=93 x=196 y=0 width=16 height=72 xoffset=-1 yoffset=16 xadvance=19 page=0 chnl=0 +char id=94 x=131 y=362 width=29 height=28 xoffset=1 yoffset=23 xadvance=30 page=0 chnl=0 +char id=95 x=306 y=362 width=34 height=8 xoffset=3 yoffset=74 xadvance=40 page=0 chnl=0 +char id=96 x=260 y=362 width=18 height=12 xoffset=1 yoffset=22 xadvance=20 page=0 chnl=0 +char id=97 x=0 y=320 width=36 height=42 xoffset=3 yoffset=36 xadvance=41 page=0 chnl=0 +char id=98 x=363 y=0 width=41 height=58 xoffset=-2 yoffset=20 xadvance=42 page=0 chnl=0 +char id=99 x=36 y=320 width=34 height=42 xoffset=2 yoffset=36 xadvance=39 page=0 chnl=0 +char id=100 x=404 y=0 width=40 height=58 xoffset=2 yoffset=20 xadvance=43 page=0 chnl=0 +char id=101 x=70 y=320 width=34 height=42 xoffset=2 yoffset=36 xadvance=39 page=0 chnl=0 +char id=102 x=444 y=0 width=26 height=58 xoffset=1 yoffset=19 xadvance=25 page=0 chnl=0 +char id=103 x=31 y=98 width=34 height=57 xoffset=2 yoffset=36 xadvance=40 page=0 chnl=0 +char id=104 x=65 y=98 width=44 height=57 xoffset=1 yoffset=20 xadvance=46 page=0 chnl=0 +char id=105 x=109 y=98 width=20 height=57 xoffset=2 yoffset=20 xadvance=23 page=0 chnl=0 +char id=106 x=112 y=0 width=18 height=73 xoffset=-2 yoffset=20 xadvance=20 page=0 chnl=0 +char id=107 x=129 y=98 width=42 height=57 xoffset=1 yoffset=20 xadvance=44 page=0 chnl=0 +char id=108 x=171 y=98 width=20 height=57 xoffset=1 yoffset=20 xadvance=22 page=0 chnl=0 +char id=109 x=171 y=320 width=66 height=41 xoffset=1 yoffset=36 xadvance=68 page=0 chnl=0 +char id=110 x=237 y=320 width=44 height=41 xoffset=1 yoffset=36 xadvance=46 page=0 chnl=0 +char id=111 x=104 y=320 width=36 height=42 xoffset=2 yoffset=36 xadvance=40 page=0 chnl=0 +char id=112 x=361 y=98 width=40 height=56 xoffset=1 yoffset=36 xadvance=43 page=0 chnl=0 +char id=113 x=401 y=98 width=39 height=56 xoffset=2 yoffset=36 xadvance=40 page=0 chnl=0 +char id=114 x=484 y=266 width=27 height=41 xoffset=2 yoffset=36 xadvance=30 page=0 chnl=0 +char id=115 x=140 y=320 width=31 height=42 xoffset=3 yoffset=36 xadvance=36 page=0 chnl=0 +char id=116 x=460 y=266 width=24 height=51 xoffset=1 yoffset=27 xadvance=26 page=0 chnl=0 +char id=117 x=281 y=320 width=43 height=41 xoffset=0 yoffset=37 xadvance=44 page=0 chnl=0 +char id=118 x=324 y=320 width=39 height=40 xoffset=0 yoffset=37 xadvance=40 page=0 chnl=0 +char id=119 x=363 y=320 width=57 height=40 xoffset=1 yoffset=37 xadvance=59 page=0 chnl=0 +char id=120 x=420 y=320 width=40 height=40 xoffset=1 yoffset=37 xadvance=42 page=0 chnl=0 +char id=121 x=440 y=98 width=40 height=56 xoffset=0 yoffset=37 xadvance=41 page=0 chnl=0 +char id=122 x=460 y=320 width=32 height=40 xoffset=3 yoffset=37 xadvance=38 page=0 chnl=0 +char id=123 x=130 y=0 width=25 height=73 xoffset=1 yoffset=18 xadvance=25 page=0 chnl=0 +char id=124 x=355 y=0 width=8 height=63 xoffset=4 yoffset=23 xadvance=16 page=0 chnl=0 +char id=125 x=155 y=0 width=25 height=73 xoffset=-1 yoffset=18 xadvance=25 page=0 chnl=0 +char id=126 x=218 y=362 width=42 height=16 xoffset=3 yoffset=47 xadvance=49 page=0 chnl=0 +char id=127 x=0 y=0 width=70 height=98 xoffset=0 yoffset=-1 xadvance=70 page=0 chnl=0 +kernings count=389 +kerning first=86 second=45 amount=-1 +kerning first=114 second=46 amount=-4 +kerning first=40 second=87 amount=1 +kerning first=70 second=99 amount=-1 +kerning first=84 second=110 amount=-3 +kerning first=114 second=116 amount=1 +kerning first=39 second=65 amount=-4 +kerning first=104 second=34 amount=-1 +kerning first=89 second=71 amount=-1 +kerning first=107 second=113 amount=-1 +kerning first=78 second=88 amount=1 +kerning first=109 second=39 amount=-1 +kerning first=120 second=100 amount=-1 +kerning first=84 second=100 amount=-3 +kerning first=68 second=90 amount=-1 +kerning first=68 second=44 amount=-4 +kerning first=84 second=103 amount=-3 +kerning first=34 second=97 amount=-2 +kerning first=70 second=97 amount=-1 +kerning first=76 second=81 amount=-2 +kerning first=73 second=89 amount=-1 +kerning first=84 second=44 amount=-8 +kerning first=68 second=65 amount=-3 +kerning first=97 second=34 amount=-2 +kerning first=111 second=121 amount=-1 +kerning first=79 second=90 amount=-1 +kerning first=75 second=121 amount=-1 +kerning first=75 second=118 amount=-1 +kerning first=111 second=118 amount=-1 +kerning first=89 second=65 amount=-9 +kerning first=75 second=71 amount=-4 +kerning first=39 second=99 amount=-2 +kerning first=75 second=99 amount=-1 +kerning first=90 second=121 amount=-1 +kerning first=44 second=39 amount=-6 +kerning first=89 second=46 amount=-7 +kerning first=89 second=74 amount=-7 +kerning first=34 second=103 amount=-2 +kerning first=70 second=103 amount=-1 +kerning first=112 second=39 amount=-1 +kerning first=122 second=113 amount=-1 +kerning first=86 second=113 amount=-2 +kerning first=68 second=84 amount=-1 +kerning first=89 second=110 amount=-1 +kerning first=34 second=100 amount=-2 +kerning first=68 second=86 amount=-1 +kerning first=87 second=45 amount=-2 +kerning first=39 second=34 amount=-4 +kerning first=114 second=100 amount=-1 +kerning first=84 second=81 amount=-1 +kerning first=70 second=101 amount=-1 +kerning first=68 second=89 amount=-2 +kerning first=88 second=117 amount=-1 +kerning first=112 second=34 amount=-1 +kerning first=76 second=67 amount=-2 +kerning first=76 second=34 amount=-5 +kerning first=88 second=111 amount=-1 +kerning first=66 second=86 amount=-1 +kerning first=66 second=89 amount=-2 +kerning first=122 second=101 amount=-1 +kerning first=86 second=101 amount=-2 +kerning first=76 second=121 amount=-5 +kerning first=84 second=119 amount=-2 +kerning first=84 second=112 amount=-3 +kerning first=87 second=111 amount=-1 +kerning first=69 second=118 amount=-1 +kerning first=65 second=117 amount=-2 +kerning first=65 second=89 amount=-9 +kerning first=72 second=89 amount=-1 +kerning first=119 second=44 amount=-4 +kerning first=69 second=121 amount=-1 +kerning first=84 second=109 amount=-3 +kerning first=84 second=122 amount=-2 +kerning first=89 second=99 amount=-2 +kerning first=76 second=118 amount=-5 +kerning first=90 second=99 amount=-1 +kerning first=90 second=103 amount=-1 +kerning first=79 second=89 amount=-2 +kerning first=90 second=79 amount=-1 +kerning first=84 second=115 amount=-4 +kerning first=76 second=65 amount=1 +kerning first=90 second=100 amount=-1 +kerning first=118 second=46 amount=-4 +kerning first=87 second=117 amount=-1 +kerning first=118 second=34 amount=1 +kerning first=69 second=103 amount=-1 +kerning first=97 second=121 amount=-1 +kerning first=39 second=111 amount=-2 +kerning first=72 second=88 amount=1 +kerning first=76 second=87 amount=-5 +kerning first=69 second=119 amount=-1 +kerning first=121 second=97 amount=-1 +kerning first=75 second=45 amount=-8 +kerning first=65 second=86 amount=-9 +kerning first=46 second=34 amount=-6 +kerning first=76 second=84 amount=-10 +kerning first=116 second=111 amount=-1 +kerning first=87 second=113 amount=-1 +kerning first=69 second=100 amount=-1 +kerning first=97 second=118 amount=-1 +kerning first=65 second=85 amount=-2 +kerning first=90 second=71 amount=-1 +kerning first=68 second=46 amount=-4 +kerning first=65 second=79 amount=-3 +kerning first=98 second=122 amount=-1 +kerning first=86 second=41 amount=1 +kerning first=84 second=118 amount=-3 +kerning first=70 second=118 amount=-1 +kerning first=121 second=111 amount=-1 +kerning first=81 second=87 amount=-1 +kerning first=70 second=100 amount=-1 +kerning first=102 second=93 amount=1 +kerning first=114 second=101 amount=-1 +kerning first=88 second=45 amount=-2 +kerning first=39 second=103 amount=-2 +kerning first=75 second=103 amount=-1 +kerning first=88 second=101 amount=-1 +kerning first=89 second=103 amount=-2 +kerning first=110 second=39 amount=-1 +kerning first=89 second=89 amount=1 +kerning first=87 second=65 amount=-2 +kerning first=119 second=46 amount=-4 +kerning first=34 second=34 amount=-4 +kerning first=88 second=79 amount=-2 +kerning first=79 second=86 amount=-1 +kerning first=76 second=119 amount=-3 +kerning first=75 second=111 amount=-1 +kerning first=65 second=116 amount=-4 +kerning first=86 second=65 amount=-9 +kerning first=70 second=84 amount=1 +kerning first=75 second=117 amount=-1 +kerning first=80 second=65 amount=-9 +kerning first=34 second=112 amount=-1 +kerning first=102 second=99 amount=-1 +kerning first=118 second=97 amount=-1 +kerning first=89 second=81 amount=-1 +kerning first=118 second=111 amount=-1 +kerning first=102 second=101 amount=-1 +kerning first=114 second=44 amount=-4 +kerning first=90 second=119 amount=-1 +kerning first=75 second=81 amount=-4 +kerning first=88 second=121 amount=-1 +kerning first=34 second=110 amount=-1 +kerning first=86 second=100 amount=-2 +kerning first=122 second=100 amount=-1 +kerning first=89 second=67 amount=-1 +kerning first=90 second=118 amount=-1 +kerning first=84 second=84 amount=1 +kerning first=121 second=34 amount=1 +kerning first=91 second=74 amount=-1 +kerning first=88 second=113 amount=-1 +kerning first=77 second=88 amount=1 +kerning first=75 second=119 amount=-2 +kerning first=114 second=104 amount=-1 +kerning first=68 second=88 amount=-2 +kerning first=121 second=44 amount=-4 +kerning first=81 second=89 amount=-1 +kerning first=102 second=39 amount=1 +kerning first=74 second=65 amount=-2 +kerning first=114 second=118 amount=1 +kerning first=84 second=46 amount=-8 +kerning first=111 second=34 amount=-1 +kerning first=88 second=71 amount=-2 +kerning first=88 second=99 amount=-1 +kerning first=84 second=74 amount=-8 +kerning first=39 second=109 amount=-1 +kerning first=98 second=34 amount=-1 +kerning first=86 second=114 amount=-1 +kerning first=88 second=81 amount=-2 +kerning first=70 second=74 amount=-11 +kerning first=89 second=83 amount=-1 +kerning first=87 second=41 amount=1 +kerning first=89 second=97 amount=-3 +kerning first=89 second=87 amount=1 +kerning first=67 second=125 amount=-1 +kerning first=89 second=93 amount=1 +kerning first=80 second=118 amount=1 +kerning first=107 second=100 amount=-1 +kerning first=114 second=34 amount=1 +kerning first=89 second=109 amount=-1 +kerning first=89 second=45 amount=-2 +kerning first=70 second=44 amount=-8 +kerning first=34 second=39 amount=-4 +kerning first=88 second=67 amount=-2 +kerning first=70 second=46 amount=-8 +kerning first=102 second=41 amount=1 +kerning first=89 second=117 amount=-1 +kerning first=89 second=111 amount=-4 +kerning first=89 second=115 amount=-4 +kerning first=114 second=102 amount=1 +kerning first=89 second=125 amount=1 +kerning first=89 second=121 amount=-1 +kerning first=114 second=108 amount=-1 +kerning first=47 second=47 amount=-8 +kerning first=65 second=63 amount=-2 +kerning first=75 second=67 amount=-4 +kerning first=87 second=100 amount=-1 +kerning first=111 second=104 amount=-1 +kerning first=111 second=107 amount=-1 +kerning first=75 second=109 amount=-1 +kerning first=87 second=114 amount=-1 +kerning first=111 second=120 amount=-1 +kerning first=69 second=99 amount=-1 +kerning first=65 second=84 amount=-6 +kerning first=39 second=97 amount=-2 +kerning first=121 second=46 amount=-4 +kerning first=89 second=85 amount=-3 +kerning first=75 second=79 amount=-4 +kerning first=107 second=99 amount=-1 +kerning first=102 second=100 amount=-1 +kerning first=102 second=103 amount=-1 +kerning first=75 second=110 amount=-1 +kerning first=39 second=110 amount=-1 +kerning first=69 second=84 amount=1 +kerning first=84 second=111 amount=-3 +kerning first=120 second=111 amount=-1 +kerning first=84 second=114 amount=-3 +kerning first=112 second=120 amount=-1 +kerning first=79 second=84 amount=-1 +kerning first=84 second=117 amount=-3 +kerning first=89 second=79 amount=-1 +kerning first=75 second=113 amount=-1 +kerning first=39 second=113 amount=-2 +kerning first=80 second=44 amount=-11 +kerning first=79 second=88 amount=-2 +kerning first=98 second=39 amount=-1 +kerning first=65 second=118 amount=-4 +kerning first=65 second=34 amount=-4 +kerning first=88 second=103 amount=-1 +kerning first=77 second=89 amount=-1 +kerning first=39 second=101 amount=-2 +kerning first=75 second=101 amount=-1 +kerning first=88 second=100 amount=-1 +kerning first=78 second=65 amount=-3 +kerning first=87 second=44 amount=-4 +kerning first=67 second=41 amount=-1 +kerning first=86 second=93 amount=1 +kerning first=84 second=83 amount=-1 +kerning first=102 second=113 amount=-1 +kerning first=34 second=111 amount=-2 +kerning first=70 second=111 amount=-1 +kerning first=86 second=99 amount=-2 +kerning first=84 second=86 amount=1 +kerning first=122 second=99 amount=-1 +kerning first=84 second=89 amount=1 +kerning first=70 second=114 amount=-1 +kerning first=86 second=74 amount=-8 +kerning first=89 second=38 amount=-1 +kerning first=87 second=97 amount=-1 +kerning first=76 second=86 amount=-9 +kerning first=40 second=86 amount=1 +kerning first=90 second=113 amount=-1 +kerning first=39 second=39 amount=-4 +kerning first=111 second=39 amount=-1 +kerning first=90 second=117 amount=-1 +kerning first=89 second=41 amount=1 +kerning first=65 second=121 amount=-4 +kerning first=89 second=100 amount=-2 +kerning first=89 second=42 amount=-2 +kerning first=76 second=117 amount=-2 +kerning first=69 second=111 amount=-1 +kerning first=46 second=39 amount=-6 +kerning first=118 second=39 amount=1 +kerning first=91 second=85 amount=-1 +kerning first=80 second=90 amount=-1 +kerning first=90 second=81 amount=-1 +kerning first=69 second=117 amount=-1 +kerning first=76 second=39 amount=-5 +kerning first=90 second=67 amount=-1 +kerning first=87 second=103 amount=-1 +kerning first=84 second=120 amount=-3 +kerning first=89 second=101 amount=-2 +kerning first=102 second=125 amount=1 +kerning first=76 second=85 amount=-2 +kerning first=79 second=65 amount=-3 +kerning first=65 second=71 amount=-3 +kerning first=79 second=44 amount=-4 +kerning first=97 second=39 amount=-2 +kerning first=90 second=101 amount=-1 +kerning first=65 second=87 amount=-5 +kerning first=79 second=46 amount=-4 +kerning first=87 second=99 amount=-1 +kerning first=34 second=101 amount=-2 +kerning first=40 second=89 amount=1 +kerning first=76 second=89 amount=-8 +kerning first=69 second=113 amount=-1 +kerning first=120 second=103 amount=-1 +kerning first=69 second=101 amount=-1 +kerning first=69 second=102 amount=-1 +kerning first=104 second=39 amount=-1 +kerning first=80 second=121 amount=1 +kerning first=86 second=46 amount=-8 +kerning first=65 second=81 amount=-3 +kerning first=86 second=44 amount=-8 +kerning first=120 second=99 amount=-1 +kerning first=98 second=120 amount=-1 +kerning first=39 second=115 amount=-3 +kerning first=121 second=39 amount=1 +kerning first=88 second=118 amount=-1 +kerning first=84 second=65 amount=-6 +kerning first=65 second=39 amount=-4 +kerning first=84 second=79 amount=-1 +kerning first=65 second=119 amount=-4 +kerning first=70 second=117 amount=-1 +kerning first=75 second=100 amount=-1 +kerning first=86 second=111 amount=-2 +kerning first=122 second=111 amount=-1 +kerning first=81 second=84 amount=-2 +kerning first=107 second=103 amount=-1 +kerning first=118 second=44 amount=-4 +kerning first=87 second=46 amount=-4 +kerning first=87 second=101 amount=-1 +kerning first=70 second=79 amount=-2 +kerning first=87 second=74 amount=-2 +kerning first=123 second=74 amount=-1 +kerning first=76 second=71 amount=-2 +kerning first=39 second=100 amount=-2 +kerning first=80 second=88 amount=-1 +kerning first=84 second=121 amount=-3 +kerning first=112 second=122 amount=-1 +kerning first=84 second=71 amount=-1 +kerning first=89 second=86 amount=1 +kerning first=84 second=113 amount=-3 +kerning first=120 second=113 amount=-1 +kerning first=89 second=44 amount=-7 +kerning first=84 second=99 amount=-3 +kerning first=34 second=113 amount=-2 +kerning first=80 second=46 amount=-11 +kerning first=86 second=117 amount=-1 +kerning first=110 second=34 amount=-1 +kerning first=80 second=74 amount=-7 +kerning first=120 second=101 amount=-1 +kerning first=73 second=88 amount=1 +kerning first=108 second=111 amount=-1 +kerning first=34 second=115 amount=-3 +kerning first=89 second=113 amount=-2 +kerning first=82 second=86 amount=-3 +kerning first=114 second=39 amount=1 +kerning first=34 second=109 amount=-1 +kerning first=84 second=101 amount=-3 +kerning first=70 second=121 amount=-1 +kerning first=123 second=85 amount=-1 +kerning first=122 second=103 amount=-1 +kerning first=86 second=97 amount=-2 +kerning first=82 second=89 amount=-4 +kerning first=66 second=84 amount=-1 +kerning first=84 second=97 amount=-4 +kerning first=86 second=103 amount=-2 +kerning first=70 second=113 amount=-1 +kerning first=84 second=87 amount=1 +kerning first=75 second=112 amount=-1 +kerning first=114 second=111 amount=-1 +kerning first=39 second=112 amount=-1 +kerning first=107 second=101 amount=-1 +kerning first=82 second=84 amount=-3 +kerning first=114 second=121 amount=1 +kerning first=34 second=99 amount=-2 +kerning first=70 second=81 amount=-2 +kerning first=111 second=122 amount=-1 +kerning first=84 second=67 amount=-1 +kerning first=111 second=108 amount=-1 +kerning first=89 second=84 amount=1 +kerning first=76 second=79 amount=-2 +kerning first=85 second=65 amount=-2 +kerning first=44 second=34 amount=-6 +kerning first=65 second=67 amount=-3 +kerning first=109 second=34 amount=-1 +kerning first=114 second=103 amount=-1 +kerning first=78 second=89 amount=-1 +kerning first=89 second=114 amount=-1 +kerning first=89 second=112 amount=-1 +kerning first=34 second=65 amount=-4 +kerning first=70 second=65 amount=-11 +kerning first=81 second=86 amount=-1 +kerning first=114 second=119 amount=1 +kerning first=89 second=102 amount=-1 +kerning first=84 second=45 amount=-8 +kerning first=86 second=125 amount=1 +kerning first=70 second=67 amount=-2 +kerning first=89 second=116 amount=-1 +kerning first=102 second=34 amount=1 +kerning first=114 second=99 amount=-1 +kerning first=67 second=84 amount=-1 +kerning first=114 second=113 amount=-1 +kerning first=89 second=122 amount=-1 +kerning first=89 second=118 amount=-1 +kerning first=70 second=71 amount=-2 +kerning first=114 second=107 amount=-1 +kerning first=89 second=120 amount=-1 diff --git a/src/web/static/fonts/bmfonts/RobotoSlab72White.png b/src/web/static/fonts/bmfonts/RobotoSlab72White.png new file mode 100644 index 00000000..5aa1bf06 Binary files /dev/null and b/src/web/static/fonts/bmfonts/RobotoSlab72White.png differ diff --git a/src/web/stylesheets/components/_operation.css b/src/web/stylesheets/components/_operation.css index a7e89cd4..a4255fc3 100755 --- a/src/web/stylesheets/components/_operation.css +++ b/src/web/stylesheets/components/_operation.css @@ -82,11 +82,47 @@ div.toggle-string { .operation .is-focused [class*=' bmd-label'], .operation .is-focused label, .operation .checkbox label:hover { - color: #1976d2; + color: var(--input-highlight-colour); +} + +.ingredients .checkbox label input[type=checkbox]+.checkbox-decorator .check, +.ingredients .checkbox label input[type=checkbox]+.checkbox-decorator .check::before { + border-color: var(--input-border-colour); + color: var(--input-highlight-colour); +} + +.ingredients .checkbox label input[type=checkbox]:checked+.checkbox-decorator .check, +.ingredients .checkbox label input[type=checkbox]:checked+.checkbox-decorator .check::before { + border-color: var(--input-highlight-colour); + color: var(--input-highlight-colour); +} + +.disabled .ingredients .checkbox label input[type=checkbox]+.checkbox-decorator .check, +.disabled .ingredients .checkbox label input[type=checkbox]+.checkbox-decorator .check::before, +.disabled .ingredients .checkbox label input[type=checkbox]:checked+.checkbox-decorator .check, +.disabled .ingredients .checkbox label input[type=checkbox]:checked+.checkbox-decorator .check::before { + border-color: var(--disabled-font-colour); + color: var(--disabled-font-colour); +} + +.break .ingredients .checkbox label input[type=checkbox]+.checkbox-decorator .check, +.break .ingredients .checkbox label input[type=checkbox]+.checkbox-decorator .check::before, +.break .ingredients .checkbox label input[type=checkbox]:checked+.checkbox-decorator .check, +.break .ingredients .checkbox label input[type=checkbox]:checked+.checkbox-decorator .check::before { + border-color: var(--breakpoint-font-colour); + color: var(--breakpoint-font-colour); +} + +.flow-control-op.break .ingredients .checkbox label input[type=checkbox]+.checkbox-decorator .check, +.flow-control-op.break .ingredients .checkbox label input[type=checkbox]+.checkbox-decorator .check::before, +.flow-control-op.break .ingredients .checkbox label input[type=checkbox]:checked+.checkbox-decorator .check, +.flow-control-op.break .ingredients .checkbox label input[type=checkbox]:checked+.checkbox-decorator .check::before { + border-color: var(--fc-breakpoint-operation-font-colour); + color: var(--fc-breakpoint-operation-font-colour); } .operation .form-control { - padding: 20px 12px 6px 12px; + padding: 20px 12px 6px 12px !important; border-top-left-radius: 4px; border-top-right-radius: 4px; background-image: none; @@ -97,7 +133,7 @@ div.toggle-string { .operation .form-control:hover { background-image: - linear-gradient(to top, #1976d2 2px, rgba(25, 118, 210, 0) 2px), + linear-gradient(to top, var(--input-highlight-colour) 2px, rgba(25, 118, 210, 0) 2px), linear-gradient(to top, rgba(0, 0, 0, 0.26) 1px, rgba(0, 0, 0, 0) 1px); filter: brightness(97%); } @@ -105,7 +141,7 @@ div.toggle-string { .operation .form-control:focus { background-color: var(--arg-background); background-image: - linear-gradient(to top, #1976d2 2px, rgba(25, 118, 210, 0) 2px), + linear-gradient(to top, var(--input-highlight-colour) 2px, rgba(25, 118, 210, 0) 2px), linear-gradient(to top, rgba(0, 0, 0, 0.26) 1px, rgba(0, 0, 0, 0) 1px); filter: brightness(100%); } @@ -205,19 +241,19 @@ div.toggle-string { } .disable-icon { - color: #9e9e9e; + color: var(--disable-icon-colour); } .disable-icon-selected { - color: #f44336; + color: var(--disable-icon-selected-colour); } .breakpoint { - color: #9e9e9e; + color: var(--breakpoint-icon-colour); } .breakpoint-selected { - color: #f44336; + color: var(--breakpoint-icon-selected-colour); } .break { diff --git a/src/web/stylesheets/components/_pane.css b/src/web/stylesheets/components/_pane.css index f98e2f3f..9ee8f46f 100755 --- a/src/web/stylesheets/components/_pane.css +++ b/src/web/stylesheets/components/_pane.css @@ -8,6 +8,7 @@ :root { --title-height: 48px; + --tab-height: 40px; } .title { @@ -52,6 +53,7 @@ line-height: 30px; background-color: var(--primary-background-colour); flex-direction: row; + padding-left: 10px; } .io-card.card:hover { @@ -60,10 +62,16 @@ .io-card.card>img { float: left; - width: 128px; - height: 128px; - margin-left: 10px; - margin-top: 11px; + width: auto; + height: auto; + max-width: 128px; + max-height: 128px; + margin-left: auto; + margin-top: auto; + margin-right: auto; + margin-bottom: auto; + padding: 0px; + } .io-card.card .card-body .close { diff --git a/src/web/stylesheets/index.css b/src/web/stylesheets/index.css index b5463a5b..ef35d54f 100755 --- a/src/web/stylesheets/index.css +++ b/src/web/stylesheets/index.css @@ -10,6 +10,8 @@ @import "./themes/_classic.css"; @import "./themes/_dark.css"; @import "./themes/_geocities.css"; +@import "./themes/_solarizedDark.css"; +@import "./themes/_solarizedLight.css"; /* Utilities */ @import "./utils/_overrides.css"; diff --git a/src/web/stylesheets/layout/_banner.css b/src/web/stylesheets/layout/_banner.css index 220ae914..59856958 100755 --- a/src/web/stylesheets/layout/_banner.css +++ b/src/web/stylesheets/layout/_banner.css @@ -22,6 +22,10 @@ padding-right: 10px; } +#banner a { + color: var(--banner-url-colour); +} + #notice-wrapper { text-align: center; overflow: hidden; diff --git a/src/web/stylesheets/layout/_controls.css b/src/web/stylesheets/layout/_controls.css index 380cd284..d231133a 100755 --- a/src/web/stylesheets/layout/_controls.css +++ b/src/web/stylesheets/layout/_controls.css @@ -21,6 +21,14 @@ background-color: var(--secondary-background-colour); } +#controls-content { + position: relative; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + transform-origin: center left; +} + #auto-bake-label { display: inline-block; width: 100px; @@ -32,6 +40,12 @@ cursor: pointer; } +#auto-bake-label .check, +#auto-bake-label .check::before { + border-color: var(--input-highlight-colour); + color: var(--input-highlight-colour); +} + #auto-bake-label .checkbox-decorator { position: relative; } @@ -43,3 +57,15 @@ #controls .btn { border-radius: 30px; } + +.spin { + animation-name: spin; + animation-duration: 3s; + animation-iteration-count: infinite; + animation-timing-function: linear; +} + +@keyframes spin { + 0% {transform: rotate(0deg);} + 100% {transform: rotate(360deg);} +} diff --git a/src/web/stylesheets/layout/_io.css b/src/web/stylesheets/layout/_io.css index 2578b57d..d7d628cb 100755 --- a/src/web/stylesheets/layout/_io.css +++ b/src/web/stylesheets/layout/_io.css @@ -24,18 +24,172 @@ word-wrap: break-word; } +#output-wrapper{ + margin: 0; + padding: 0; +} + +#output-wrapper .textarea-wrapper { + width: 100%; + height: 100%; + box-sizing: border-box; + overflow: hidden; + pointer-events: auto; +} + + #output-html { display: none; overflow-y: auto; -moz-padding-start: 1px; /* Fixes bug in Firefox */ } -.textarea-wrapper { - position: absolute; - top: var(--title-height); - bottom: 0; +#input-tabs-wrapper #input-tabs, +#output-tabs-wrapper #output-tabs { + list-style: none; + background-color: var(--title-background-colour); + padding: 0; + margin: 0; + overflow-x: auto; + overflow-y: hidden; + display: flex; + flex-direction: row; + border-bottom: 1px solid var(--primary-border-colour); + border-left: 1px solid var(--primary-border-colour); + height: var(--tab-height); + clear: none; +} + +#input-tabs li, +#output-tabs li { + display: flex; + flex-direction: row; width: 100%; + min-width: 120px; + float: left; + padding: 0px; + text-align: center; + border-right: 1px solid var(--primary-border-colour); + height: var(--tab-height); + vertical-align: middle; +} + +#input-tabs li:hover, +#output-tabs li:hover { + cursor: pointer; + background-color: var(--primary-background-colour); +} + +.active-input-tab, +.active-output-tab { + font-weight: bold; + background-color: var(--primary-background-colour); +} + +.input-tab-content+.btn-close-tab { + display: block; + margin-top: auto; + margin-bottom: auto; + margin-right: 2px; +} + +.input-tab-content+.btn-close-tab i { + font-size: 0.8em; +} + +.input-tab-buttons, +.output-tab-buttons { + width: 25px; + text-align: center; + margin: 0; + height: var(--tab-height); + line-height: var(--tab-height); + font-weight: bold; + background-color: var(--title-background-colour); + border-bottom: 1px solid var(--primary-border-colour); +} + +.input-tab-buttons:hover, +.output-tab-buttons:hover { + cursor: pointer; + background-color: var(--primary-background-colour); +} + + +#btn-next-input-tab, +#btn-input-tab-dropdown, +#btn-next-output-tab, +#btn-output-tab-dropdown { + float: right; +} + +#btn-previous-input-tab, +#btn-previous-output-tab { + float: left; +} + +#btn-close-all-tabs { + color: var(--breakpoint-font-colour) !important; +} + +.input-tab-content, +.output-tab-content { + width: 100%; + max-width: 100%; + padding-left: 5px; + padding-right: 5px; + padding-top: 10px; + padding-bottom: 10px; + height: var(--tab-height); + vertical-align: middle; overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.btn-close-tab { + height: var(--tab-height); + vertical-align: middle; + width: fit-content; +} + +.tabs-left > li:first-child { + box-shadow: 15px 0px 15px -15px var(--primary-border-colour) inset; +} + +.tabs-right > li:last-child { + box-shadow: -15px 0px 15px -15px var(--primary-border-colour) inset; +} + +#input-wrapper, +#output-wrapper, +#input-wrapper > * , +#output-wrapper > .textarea-wrapper > div, +#output-wrapper > .textarea-wrapper > textarea { + height: calc(100% - var(--title-height)); +} + +#input-wrapper.show-tabs, +#input-wrapper.show-tabs > *, +#output-wrapper.show-tabs, +#output-wrapper.show-tabs > .textarea-wrapper > div, +#output-wrapper.show-tabs > .textarea-wrapper > textarea { + height: calc(100% - var(--tab-height) - var(--title-height)); +} + +#output-wrapper > .textarea-wrapper > #output-html { + height: 100%; +} + +#show-file-overlay { + height: 32px; +} + +.input-wrapper.textarea-wrapper { + width: 100%; + box-sizing: border-box; + overflow: hidden; + pointer-events: auto; } .textarea-wrapper textarea, @@ -49,9 +203,8 @@ #output-highlighter { position: absolute; left: 0; - top: 0; + bottom: 0; width: 100%; - height: 100%; padding: 3px; margin: 0; overflow: hidden; @@ -61,14 +214,14 @@ color: #fff; background-color: transparent; border: none; + pointer-events: none; } #output-loader { position: absolute; - top: 0; + bottom: 0; left: 0; width: 100%; - height: 100%; margin: 0; background-color: var(--primary-background-colour); visibility: hidden; @@ -105,9 +258,8 @@ #output-file { position: absolute; left: 0; - top: 0; + bottom: 0; width: 100%; - height: 100%; display: none; } @@ -122,7 +274,7 @@ #show-file-overlay { position: absolute; right: 15px; - top: 15px; + top: calc(var(--title-height) + 10px); cursor: pointer; display: none; } @@ -147,7 +299,6 @@ .dropping-file { border: 5px dashed var(--drop-file-border-colour) !important; - margin: -5px; } #stale-indicator { @@ -185,3 +336,103 @@ #magic svg path { fill: var(--primary-font-colour); } + +#input-find-options, +#output-find-options { + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 100%; +} + +#input-tab-body .form-group.input-group, +#output-tab-body .form-group.input-group { + width: 70%; + float: left; + margin-bottom: 2rem; +} + +.input-find-option .toggle-string { + width: 70%; + display: inline-block; +} + +.input-find-option-append button { + border-top-right-radius: 4px; + background-color: var(--arg-background) !important; + margin: unset; +} + +.input-find-option-append button:hover { + filter: brightness(97%); +} + +.form-group.output-find-option { + width: 70%; + float: left; +} + +#input-num-results-container, +#output-num-results-container { + width: 20%; + float: right; + margin: 0; + margin-left: 10%; +} + +#input-find-options-checkboxes, +#output-find-options-checkboxes { + list-style: none; + padding: 0; + margin: auto; + overflow-x: auto; + overflow-y: hidden; + text-align: center; + width: fit-content; +} + +#input-find-options-checkboxes li, +#output-find-options-checkboxes li { + display: flex; + flex-direction: row; + float: left; + padding: 10px; + text-align: center; +} + + +#input-search-results, +#output-search-results { + list-style: none; + width: 75%; + min-width: 200px; + margin-left: auto; + margin-right: auto; +} + +#input-search-results li, +#output-search-results li { + padding-left: 5px; + padding-right: 5px; + padding-top: 10px; + padding-bottom: 10px; + text-align: center; + width: 100%; + color: var(--op-list-operation-font-colour); + background-color: var(--op-list-operation-bg-colour); + border-bottom: 2px solid var(--op-list-operation-border-colour); + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +#input-search-results li:first-of-type, +#output-search-results li:first-of-type { + border-top: 2px solid var(--op-list-operation-border-colour); +} + +#input-search-results li:hover, +#output-search-results li:hover { + cursor: pointer; + filter: brightness(98%); +} diff --git a/src/web/stylesheets/layout/_modals.css b/src/web/stylesheets/layout/_modals.css index a49c579d..c1745eeb 100755 --- a/src/web/stylesheets/layout/_modals.css +++ b/src/web/stylesheets/layout/_modals.css @@ -77,3 +77,34 @@ padding: 20px; border-left: 2px solid var(--primary-border-colour); } + +.checkbox label input[type=checkbox]+.checkbox-decorator .check, +.checkbox label input[type=checkbox]+.checkbox-decorator .check::before { + border-color: var(--input-border-colour); + color: var(--input-highlight-colour); +} + +.checkbox label input[type=checkbox]:checked+.checkbox-decorator .check, +.checkbox label input[type=checkbox]:checked+.checkbox-decorator .check::before { + border-color: var(--input-highlight-colour); + color: var(--input-highlight-colour); +} + +.bmd-form-group.is-focused .option-item label { + color: var(--input-highlight-colour); +} + +.bmd-form-group.is-focused [class^='bmd-label'], +.bmd-form-group.is-focused [class*=' bmd-label'], +.bmd-form-group.is-focused [class^='bmd-label'], +.bmd-form-group.is-focused [class*=' bmd-label'], +.bmd-form-group.is-focused label, +.checkbox label:hover { + color: var(--input-highlight-colour); +} + +.bmd-form-group.option-item label+.form-control{ + background-image: + linear-gradient(to top, var(--input-highlight-colour) 2px, rgba(0, 0, 0, 0) 2px), + linear-gradient(to top, var(--primary-border-colour) 1px, rgba(0, 0, 0, 0) 1px); +} \ No newline at end of file diff --git a/src/web/stylesheets/layout/_operations.css b/src/web/stylesheets/layout/_operations.css index e5e4c887..b73dfa84 100755 --- a/src/web/stylesheets/layout/_operations.css +++ b/src/web/stylesheets/layout/_operations.css @@ -16,7 +16,7 @@ padding-left: 10px; padding-right: 10px; background-image: - linear-gradient(to top, #1976d2 2px, rgba(25, 118, 210, 0) 2px), + linear-gradient(to top, var(--input-highlight-colour) 2px, rgba(0, 0, 0, 0) 2px), linear-gradient(to top, var(--primary-border-colour) 1px, rgba(0, 0, 0, 0) 1px); } @@ -33,7 +33,7 @@ } #categories a { - color: #1976d2; + color: var(--category-list-font-colour); cursor: pointer; } diff --git a/src/web/stylesheets/preloader.css b/src/web/stylesheets/preloader.css index 288ffc28..01e6d3d2 100755 --- a/src/web/stylesheets/preloader.css +++ b/src/web/stylesheets/preloader.css @@ -6,14 +6,14 @@ * @license Apache-2.0 */ -#loader-wrapper { + #loader-wrapper { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000; - background-color: var(--secondary-border-colour); + background-color: var(--loader-background-colour); } .loader { @@ -26,7 +26,7 @@ margin: -75px 0 0 -75px; border: 3px solid transparent; - border-top-color: #3498db; + border-top-color: var(--loader-outer-colour); border-radius: 50%; animation: spin 2s linear infinite; @@ -45,7 +45,7 @@ left: 5px; right: 5px; bottom: 5px; - border-top-color: #e74c3c; + border-top-color: var(--loader-middle-colour); animation: spin 3s linear infinite; } @@ -54,7 +54,7 @@ left: 13px; right: 13px; bottom: 13px; - border-top-color: #f9c922; + border-top-color: var(--loader-inner-colour); animation: spin 1.5s linear infinite; } diff --git a/src/web/stylesheets/themes/_classic.css b/src/web/stylesheets/themes/_classic.css index 5d6bd3e8..3b3bd555 100755 --- a/src/web/stylesheets/themes/_classic.css +++ b/src/web/stylesheets/themes/_classic.css @@ -35,6 +35,14 @@ --banner-font-colour: #468847; --banner-bg-colour: #dff0d8; + --banner-url-colour: #1976d2; + + --category-list-font-colour: #1976d2; + + --loader-background-colour: var(--secondary-border-colour); + --loader-outer-colour: #3498db; + --loader-middle-colour: #e74c3c; + --loader-inner-colour: #f9c922; /* Operation colours */ @@ -76,6 +84,13 @@ --arg-label-colour: #388e3c; + /* Operation buttons */ + --disable-icon-colour: #9e9e9e; + --disable-icon-selected-colour: #f44336; + --breakpoint-icon-colour: #9e9e9e; + --breakpoint-icon-selected-colour: #f44336; + + /* Buttons */ --btn-default-font-colour: #333; --btn-default-bg-colour: #fff; @@ -110,8 +125,11 @@ /* Misc. */ --drop-file-border-colour: #3a87ad; + --table-border-colour: #ccc; --popover-background: #fff; --popover-border-colour: #ccc; --code-background: #f9f2f4; --code-font-colour: #c7254e; + --input-highlight-colour: #1976d2; + --input-border-colour: #424242; } diff --git a/src/web/stylesheets/themes/_dark.css b/src/web/stylesheets/themes/_dark.css index 62d91808..10340ea8 100755 --- a/src/web/stylesheets/themes/_dark.css +++ b/src/web/stylesheets/themes/_dark.css @@ -31,6 +31,14 @@ --banner-font-colour: #c5c5c5; --banner-bg-colour: #252525; + --banner-url-colour: #1976d2; + + --category-list-font-colour: #1976d2; + + --loader-background-colour: var(--secondary-border-colour); + --loader-outer-colour: #3498db; + --loader-middle-colour: #e74c3c; + --loader-inner-colour: #f9c922; /* Operation colours */ @@ -72,6 +80,13 @@ --arg-label-colour: rgb(25, 118, 210); + /* Operation buttons */ + --disable-icon-colour: #9e9e9e; + --disable-icon-selected-colour: #f44336; + --breakpoint-icon-colour: #9e9e9e; + --breakpoint-icon-selected-colour: #f44336; + + /* Buttons */ --btn-default-font-colour: #c5c5c5; --btn-default-bg-colour: #2d2d2d; @@ -106,8 +121,11 @@ /* Misc. */ --drop-file-border-colour: #0e639c; + --table-border-colour: #555; --popover-background: #444; --popover-border-colour: #555; --code-background: #0e639c; --code-font-colour: #fff; + --input-highlight-colour: #1976d2; + --input-border-colour: #424242; } diff --git a/src/web/stylesheets/themes/_geocities.css b/src/web/stylesheets/themes/_geocities.css index 12936e21..fcf3cdb5 100755 --- a/src/web/stylesheets/themes/_geocities.css +++ b/src/web/stylesheets/themes/_geocities.css @@ -31,6 +31,14 @@ --banner-font-colour: white; --banner-bg-colour: maroon; + --banner-url-colour: yellow; + + --category-list-font-colour: yellow; + + --loader-background-colour: #00f; + --loader-outer-colour: #0f0; + --loader-middle-colour: red; + --loader-inner-colour: yellow; /* Operation colours */ @@ -72,6 +80,13 @@ --arg-label-colour: red; + /* Operation buttons */ + --disable-icon-colour: #0f0; + --disable-icon-selected-colour: yellow; + --breakpoint-icon-colour: #0f0; + --breakpoint-icon-selected-colour: yellow; + + /* Buttons */ --btn-default-font-colour: black; --btn-default-bg-colour: white; @@ -106,8 +121,11 @@ /* Misc. */ --drop-file-border-colour: purple; + --table-border-colour: var(--hl3); --popover-background: turquoise; --popover-border-colour: violet; --code-background: black; --code-font-colour: limegreen; + --input-highlight-colour: limegreen; + --input-border-colour: limegreen; } diff --git a/src/web/stylesheets/themes/_solarizedDark.css b/src/web/stylesheets/themes/_solarizedDark.css new file mode 100755 index 00000000..3b7d4338 --- /dev/null +++ b/src/web/stylesheets/themes/_solarizedDark.css @@ -0,0 +1,148 @@ +/** + * Solarized dark theme definitions + * + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +:root.solarizedDark { + --base03: #002b36; + --base02: #073642; + --base01: #586e75; + --base00: #657b83; + --base0: #839496; + --base1: #93a1a1; + --base2: #eee8d5; + --base3: #fdf6e3; + --sol-yellow: #b58900; + --sol-orange: #cb4b16; + --sol-red: #dc322f; + --sol-magenta: #d33682; + --sol-violet: #6c71c4; + --sol-blue: #268bd2; + --sol-cyan: #2aa198; + --sol-green: #859900; + + --primary-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", + Roboto, "Helvetica Neue", Arial, sans-serif; + --primary-font-colour: var(--base0); + --primary-font-size: 14px; + --primary-line-height: 20px; + + --fixed-width-font-family: SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; + --fixed-width-font-colour: inherit; + --fixed-width-font-size: inherit; + + --subtext-font-colour: var(--base01); + --subtext-font-size: 13px; + + --primary-background-colour: var(--base03); + --secondary-background-colour: var(--base02); + + --primary-border-colour: var(--base00); + --secondary-border-colour: var(--base01); + + --title-colour: var(--base1); + --title-weight: bold; + --title-background-colour: var(--base02); + + --banner-font-colour: var(--base0); + --banner-bg-colour: var(--base03); + --banner-url-colour: var(--base1); + + --category-list-font-colour: var(--base1); + + --loader-background-colour: var(--base03); + --loader-outer-colour: var(--base1); + --loader-middle-colour: var(--base0); + --loader-inner-colour: var(--base00); + + + /* Operation colours */ + --op-list-operation-font-colour: var(--base0); + --op-list-operation-bg-colour: var(--base03); + --op-list-operation-border-colour: var(--base02); + + --rec-list-operation-font-colour: var(--base0); + --rec-list-operation-bg-colour: var(--base02); + --rec-list-operation-border-colour: var(--base01); + + --selected-operation-font-color: var(--base1); + --selected-operation-bg-colour: var(--base02); + --selected-operation-border-colour: var(--base01); + + --breakpoint-font-colour: var(--sol-red); + --breakpoint-bg-colour: var(--base02); + --breakpoint-border-colour: var(--base00); + + --disabled-font-colour: var(--base01); + --disabled-bg-colour: var(--base03); + --disabled-border-colour: var(--base02); + + --fc-operation-font-colour: var(--base1); + --fc-operation-bg-colour: var(--base02); + --fc-operation-border-colour: var(--base01); + + --fc-breakpoint-operation-font-colour: var(--sol-orange); + --fc-breakpoint-operation-bg-colour: var(--base02); + --fc-breakpoint-operation-border-colour: var(--base00); + + + /* Operation arguments */ + --op-title-font-weight: bold; + --arg-font-colour: var(--base0); + --arg-background: var(--base03); + --arg-border-colour: var(--base00); + --arg-disabled-background: var(--base03); + --arg-label-colour: var(--base1); + + + /* Operation buttons */ + --disable-icon-colour: var(--base00); + --disable-icon-selected-colour: var(--sol-red); + --breakpoint-icon-colour: var(--base00); + --breakpoint-icon-selected-colour: var(--sol-red); + + /* Buttons */ + --btn-default-font-colour: var(--base0); + --btn-default-bg-colour: var(--base02); + --btn-default-border-colour: var(--base01); + + --btn-default-hover-font-colour: var(--base1); + --btn-default-hover-bg-colour: var(--base01); + --btn-default-hover-border-colour: var(--base00); + + --btn-success-font-colour: var(--base0); + --btn-success-bg-colour: var(--base03); + --btn-success-border-colour: var(--base00); + + --btn-success-hover-font-colour: var(--base1); + --btn-success-hover-bg-colour: var(--base01); + --btn-success-hover-border-colour: var(--base00); + + /* Highlighter colours */ + --hl1: var(--base01); + --hl2: var(--sol-blue); + --hl3: var(--sol-magenta); + --hl4: var(--sol-yellow); + --hl5: var(--sol-green); + + + /* Scrollbar */ + --scrollbar-track: var(--base03); + --scrollbar-thumb: var(--base00); + --scrollbar-hover: var(--base01); + + + /* Misc. */ + --drop-file-border-colour: var(--base01); + --table-border-colour: var(--base01); + --popover-background: var(--base02); + --popover-border-colour: var(--base01); + --code-background: var(--base03); + --code-font-colour: var(--base1); + --input-highlight-colour: var(--base1); + --input-border-colour: var(--base0); +} diff --git a/src/web/stylesheets/themes/_solarizedLight.css b/src/web/stylesheets/themes/_solarizedLight.css new file mode 100755 index 00000000..00b86091 --- /dev/null +++ b/src/web/stylesheets/themes/_solarizedLight.css @@ -0,0 +1,150 @@ +/** + * Solarized light theme definitions + * + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +:root.solarizedLight { + --base03: #002b36; + --base02: #073642; + --base01: #586e75; + --base00: #657b83; + --base0: #839496; + --base1: #93a1a1; + --base2: #eee8d5; + --base3: #fdf6e3; + --sol-yellow: #b58900; + --sol-orange: #cb4b16; + --sol-red: #dc322f; + --sol-magenta: #d33682; + --sol-violet: #6c71c4; + --sol-blue: #268bd2; + --sol-cyan: #2aa198; + --sol-green: #859900; + + --primary-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", + Roboto, "Helvetica Neue", Arial, sans-serif; + --primary-font-colour: var(--base00); + --primary-font-size: 14px; + --primary-line-height: 20px; + + --fixed-width-font-family: SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; + --fixed-width-font-colour: inherit; + --fixed-width-font-size: inherit; + + --subtext-font-colour: var(--base1); + --subtext-font-size: 13px; + + --primary-background-colour: var(--base3); + --secondary-background-colour: var(--base2); + + --primary-border-colour: var(--base0); + --secondary-border-colour: var(--base1); + + --title-colour: var(--base01); + --title-weight: bold; + --title-background-colour: var(--base2); + + --banner-font-colour: var(--base00); + --banner-bg-colour: var(--base3); + --banner-url-colour: var(--base01); + + --category-list-font-colour: var(--base01); + + --loader-background-colour: var(--base3); + --loader-outer-colour: var(--base01); + --loader-middle-colour: var(--base00); + --loader-inner-colour: var(--base0); + + + /* Operation colours */ + --op-list-operation-font-colour: var(--base00); + --op-list-operation-bg-colour: var(--base3); + --op-list-operation-border-colour: var(--base2); + + --rec-list-operation-font-colour: var(--base00); + --rec-list-operation-bg-colour: var(--base2); + --rec-list-operation-border-colour: var(--base1); + + --selected-operation-font-color: var(--base01); + --selected-operation-bg-colour: var(--base2); + --selected-operation-border-colour: var(--base1); + + --breakpoint-font-colour: var(--sol-red); + --breakpoint-bg-colour: var(--base2); + --breakpoint-border-colour: var(--base0); + + --disabled-font-colour: var(--base1); + --disabled-bg-colour: var(--base3); + --disabled-border-colour: var(--base2); + + --fc-operation-font-colour: var(--base01); + --fc-operation-bg-colour: var(--base2); + --fc-operation-border-colour: var(--base1); + + --fc-breakpoint-operation-font-colour: var(--base02); + --fc-breakpoint-operation-bg-colour: var(--base1); + --fc-breakpoint-operation-border-colour: var(--base0); + + + /* Operation arguments */ + --op-title-font-weight: bold; + --arg-font-colour: var(--base00); + --arg-background: var(--base3); + --arg-border-colour: var(--base0); + --arg-disabled-background: var(--base3); + --arg-label-colour: var(--base01); + + + /* Operation buttons */ + --disable-icon-colour: #9e9e9e; + --disable-icon-selected-colour: #f44336; + --breakpoint-icon-colour: #9e9e9e; + --breakpoint-icon-selected-colour: #f44336; + + + /* Buttons */ + --btn-default-font-colour: var(--base00); + --btn-default-bg-colour: var(--base2); + --btn-default-border-colour: var(--base1); + + --btn-default-hover-font-colour: var(--base01); + --btn-default-hover-bg-colour: var(--base1); + --btn-default-hover-border-colour: var(--base0); + + --btn-success-font-colour: var(--base00); + --btn-success-bg-colour: var(--base3); + --btn-success-border-colour: var(--base0); + + --btn-success-hover-font-colour: var(--base01); + --btn-success-hover-bg-colour: var(--base1); + --btn-success-hover-border-colour: var(--base0); + + + /* Highlighter colours */ + --hl1: var(--base1); + --hl2: var(--sol-blue); + --hl3: var(--sol-magenta); + --hl4: var(--sol-yellow); + --hl5: var(--sol-green); + + + /* Scrollbar */ + --scrollbar-track: var(--base3); + --scrollbar-thumb: var(--base1); + --scrollbar-hover: var(--base0); + + + /* Misc. */ + --drop-file-border-colour: var(--base1); + --table-border-colour: var(--base1); + --popover-background: var(--base2); + --popover-border-colour: var(--base1); + --code-background: var(--base3); + --code-font-colour: var(--base01); + --input-highlight-colour: var(--base01); + --input-border-colour: var(--base00); +} diff --git a/src/web/stylesheets/utils/_overrides.css b/src/web/stylesheets/utils/_overrides.css index 014da99a..b1ca4c57 100755 --- a/src/web/stylesheets/utils/_overrides.css +++ b/src/web/stylesheets/utils/_overrides.css @@ -104,8 +104,11 @@ select.form-control:not([size]):not([multiple]), select.custom-file-control:not( color: var(--primary-font-colour); } -.form-control { - background-image: linear-gradient(to top, rgb(25, 118, 210) 2px, rgba(25, 118, 210, 0) 2px), linear-gradient(to top, var(--primary-border-colour) 1px, rgba(0, 0, 0, 0) 1px); +.form-control, +.is-focused .form-control { + background-image: + linear-gradient(to top, var(--input-highlight-colour) 2px, rgba(0, 0, 0, 0) 2px), + linear-gradient(to top, var(--primary-border-colour) 1px, rgba(0, 0, 0, 0) 1px); } code { @@ -145,6 +148,15 @@ optgroup { width: auto !important; } +.table, +.table-hover tbody tr:hover { + color: var(--primary-font-colour); +} + +.table-bordered th, .table-bordered td { + border: 1px solid var(--table-border-colour); +} + .popover { background-color: var(--popover-background); border-color: var(--popover-border-colour); diff --git a/src/web/BackgroundWorkerWaiter.mjs b/src/web/waiters/BackgroundWorkerWaiter.mjs similarity index 97% rename from src/web/BackgroundWorkerWaiter.mjs rename to src/web/waiters/BackgroundWorkerWaiter.mjs index b7b259be..a7f9b83a 100644 --- a/src/web/BackgroundWorkerWaiter.mjs +++ b/src/web/waiters/BackgroundWorkerWaiter.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import ChefWorker from "worker-loader?inline&fallback=false!../core/ChefWorker"; +import ChefWorker from "worker-loader?inline&fallback=false!../../core/ChefWorker"; /** * Waiter to handle conversations with a ChefWorker in the background. @@ -68,6 +68,7 @@ class BackgroundWorkerWaiter { break; case "optionUpdate": case "statusMessage": + case "progressMessage": // Ignore these messages break; default: diff --git a/src/web/BindingsWaiter.mjs b/src/web/waiters/BindingsWaiter.mjs similarity index 85% rename from src/web/BindingsWaiter.mjs rename to src/web/waiters/BindingsWaiter.mjs index 74262c61..79c2903b 100755 --- a/src/web/BindingsWaiter.mjs +++ b/src/web/waiters/BindingsWaiter.mjs @@ -98,11 +98,11 @@ class BindingsWaiter { break; case "Space": // Bake e.preventDefault(); - this.app.bake(); + this.manager.controls.bakeClick(); break; case "Quote": // Step through e.preventDefault(); - this.app.bake(true); + this.manager.controls.stepClick(); break; case "KeyC": // Clear recipe e.preventDefault(); @@ -120,6 +120,22 @@ class BindingsWaiter { e.preventDefault(); this.manager.output.switchClick(); break; + case "KeyT": // New tab + e.preventDefault(); + this.manager.input.addInputClick(); + break; + case "KeyW": // Close tab + e.preventDefault(); + this.manager.input.removeInput(this.manager.tabs.getActiveInputTab()); + break; + case "ArrowLeft": // Go to previous tab + e.preventDefault(); + this.manager.input.changeTabLeft(); + break; + case "ArrowRight": // Go to next tab + e.preventDefault(); + this.manager.input.changeTabRight(); + break; default: if (e.code.match(/Digit[0-9]/g)) { // Select nth operation e.preventDefault(); @@ -216,6 +232,26 @@ class BindingsWaiter { Ctrl+${modWinLin}+m Ctrl+${modMac}+m + + Create a new tab + Ctrl+${modWinLin}+t + Ctrl+${modMac}+t + + + Close the current tab + Ctrl+${modWinLin}+w + Ctrl+${modMac}+w + + + Go to next tab + Ctrl+${modWinLin}+RightArrow + Ctrl+${modMac}+RightArrow + + + Go to previous tab + Ctrl+${modWinLin}+LeftArrow + Ctrl+${modMac}+LeftArrow + `; } diff --git a/src/web/ControlsWaiter.mjs b/src/web/waiters/ControlsWaiter.mjs similarity index 83% rename from src/web/ControlsWaiter.mjs rename to src/web/waiters/ControlsWaiter.mjs index bcebb1f3..3dd014b6 100755 --- a/src/web/ControlsWaiter.mjs +++ b/src/web/waiters/ControlsWaiter.mjs @@ -4,8 +4,7 @@ * @license Apache-2.0 */ -import Utils from "../core/Utils"; -import {toBase64} from "../core/lib/Base64"; +import Utils from "../../core/Utils.mjs"; /** @@ -57,10 +56,11 @@ class ControlsWaiter { * Handler to trigger baking. */ bakeClick() { - if (document.getElementById("bake").textContent.indexOf("Bake") > 0) { - this.app.bake(); - } else { - this.manager.worker.cancelBake(); + const btnBake = document.getElementById("bake"); + if (btnBake.textContent.indexOf("Bake") > 0) { + this.app.manager.input.bakeAll(); + } else if (btnBake.textContent.indexOf("Cancel") > 0) { + this.manager.worker.cancelBake(false, true); } } @@ -69,7 +69,7 @@ class ControlsWaiter { * Handler for the 'Step through' command. Executes the next step of the recipe. */ stepClick() { - this.app.bake(true); + this.app.step(); } @@ -90,7 +90,7 @@ class ControlsWaiter { /** - * Populates the save disalog box with a URL incorporating the recipe and input. + * Populates the save dialog box with a URL incorporating the recipe and input. * * @param {Object[]} [recipeConfig] - The recipe configuration object array. */ @@ -112,26 +112,33 @@ class ControlsWaiter { * * @param {boolean} includeRecipe - Whether to include the recipe in the URL. * @param {boolean} includeInput - Whether to include the input in the URL. + * @param {string} input * @param {Object[]} [recipeConfig] - The recipe configuration object array. * @param {string} [baseURL] - The CyberChef URL, set to the current URL if not included * @returns {string} */ - generateStateUrl(includeRecipe, includeInput, recipeConfig, baseURL) { + generateStateUrl(includeRecipe, includeInput, input, recipeConfig, baseURL) { recipeConfig = recipeConfig || this.app.getRecipeConfig(); const link = baseURL || window.location.protocol + "//" + window.location.host + window.location.pathname; const recipeStr = Utils.generatePrettyRecipe(recipeConfig); - const inputStr = toBase64(this.app.getInput(), "A-Za-z0-9+/"); // B64 alphabet with no padding includeRecipe = includeRecipe && (recipeConfig.length > 0); - // Only inlcude input if it is less than 50KB (51200 * 4/3 as it is Base64 encoded) - includeInput = includeInput && (inputStr.length > 0) && (inputStr.length <= 68267); + + // If we don't get passed an input, get it from the current URI + if (input === null) { + const params = this.app.getURIParams(); + if (params.input) { + includeInput = true; + input = params.input; + } + } const params = [ includeRecipe ? ["recipe", recipeStr] : undefined, - includeInput ? ["input", inputStr] : undefined, + includeInput ? ["input", input] : undefined, ]; const hash = params @@ -335,7 +342,7 @@ class ControlsWaiter { e.preventDefault(); const reportBugInfo = document.getElementById("report-bug-info"); - const saveLink = this.generateStateUrl(true, true, null, "https://gchq.github.io/CyberChef/"); + const saveLink = this.generateStateUrl(true, true, null, null, "https://gchq.github.io/CyberChef/"); if (reportBugInfo) { reportBugInfo.innerHTML = `* Version: ${PKG_VERSION} @@ -370,22 +377,34 @@ ${navigator.userAgent} /** - * Switches the Bake button between 'Bake' and 'Cancel' functions. + * Switches the Bake button between 'Bake', 'Cancel' and 'Loading' functions. * - * @param {boolean} cancel - Whether to change to cancel or not + * @param {string} func - The function to change to. Either "cancel", "loading" or "bake" */ - toggleBakeButtonFunction(cancel) { + toggleBakeButtonFunction(func) { const bakeButton = document.getElementById("bake"), btnText = bakeButton.querySelector("span"); - if (cancel) { - btnText.innerText = "Cancel"; - bakeButton.classList.remove("btn-success"); - bakeButton.classList.add("btn-danger"); - } else { - btnText.innerText = "Bake!"; - bakeButton.classList.remove("btn-danger"); - bakeButton.classList.add("btn-success"); + switch (func) { + case "cancel": + btnText.innerText = "Cancel"; + bakeButton.classList.remove("btn-success"); + bakeButton.classList.remove("btn-warning"); + bakeButton.classList.add("btn-danger"); + break; + case "loading": + bakeButton.style.background = ""; + btnText.innerText = "Loading..."; + bakeButton.classList.remove("btn-success"); + bakeButton.classList.remove("btn-danger"); + bakeButton.classList.add("btn-warning"); + break; + default: + bakeButton.style.background = ""; + btnText.innerText = "Bake!"; + bakeButton.classList.remove("btn-danger"); + bakeButton.classList.remove("btn-warning"); + bakeButton.classList.add("btn-success"); } } diff --git a/src/web/HighlighterWaiter.mjs b/src/web/waiters/HighlighterWaiter.mjs similarity index 99% rename from src/web/HighlighterWaiter.mjs rename to src/web/waiters/HighlighterWaiter.mjs index 99ae10b1..95050556 100755 --- a/src/web/HighlighterWaiter.mjs +++ b/src/web/waiters/HighlighterWaiter.mjs @@ -378,6 +378,8 @@ class HighlighterWaiter { displayHighlights(pos, direction) { if (!pos) return; + if (this.manager.tabs.getActiveInputTab() !== this.manager.tabs.getActiveOutputTab()) return; + const io = direction === "forward" ? "output" : "input"; document.getElementById(io + "-selection-info").innerHTML = this.selectionInfo(pos[0].start, pos[0].end); diff --git a/src/web/waiters/InputWaiter.mjs b/src/web/waiters/InputWaiter.mjs new file mode 100644 index 00000000..40ec412f --- /dev/null +++ b/src/web/waiters/InputWaiter.mjs @@ -0,0 +1,1378 @@ +/** + * @author n1474335 [n1474335@gmail.com] + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2016 + * @license Apache-2.0 + */ + +import LoaderWorker from "worker-loader?inline&fallback=false!../workers/LoaderWorker"; +import InputWorker from "worker-loader?inline&fallback=false!../workers/InputWorker"; +import Utils from "../../core/Utils.mjs"; +import { toBase64 } from "../../core/lib/Base64.mjs"; +import { isImage } from "../../core/lib/FileType.mjs"; + + +/** + * Waiter to handle events related to the input. + */ +class InputWaiter { + + /** + * InputWaiter constructor. + * + * @param {App} app - The main view object for CyberChef. + * @param {Manager} manager - The CyberChef event manager. + */ + constructor(app, manager) { + this.app = app; + this.manager = manager; + + // Define keys that don't change the input so we don't have to autobake when they are pressed + this.badKeys = [ + 16, //Shift + 17, //Ctrl + 18, //Alt + 19, //Pause + 20, //Caps + 27, //Esc + 33, 34, 35, 36, //PgUp, PgDn, End, Home + 37, 38, 39, 40, //Directional + 44, //PrntScrn + 91, 92, //Win + 93, //Context + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, //F1-12 + 144, //Num + 145, //Scroll + ]; + + this.inputWorker = null; + this.loaderWorkers = []; + this.workerId = 0; + this.maxTabs = this.manager.tabs.calcMaxTabs(); + this.callbacks = {}; + this.callbackID = 0; + + this.maxWorkers = 1; + if (navigator.hardwareConcurrency !== undefined && + navigator.hardwareConcurrency > 1) { + // Subtract 1 from hardwareConcurrency value to avoid using + // the entire available resources + this.maxWorkers = navigator.hardwareConcurrency - 1; + } + } + + /** + * Calculates the maximum number of tabs to display + */ + calcMaxTabs() { + const numTabs = this.manager.tabs.calcMaxTabs(); + if (this.inputWorker && this.maxTabs !== numTabs) { + this.maxTabs = numTabs; + this.inputWorker.postMessage({ + action: "updateMaxTabs", + data: { + maxTabs: numTabs, + activeTab: this.manager.tabs.getActiveInputTab() + } + }); + } + } + + /** + * Terminates any existing workers and sets up a new InputWorker and LoaderWorker + */ + setupInputWorker() { + if (this.inputWorker !== null) { + this.inputWorker.terminate(); + this.inputWorker = null; + } + + for (let i = this.loaderWorkers.length - 1; i >= 0; i--) { + this.removeLoaderWorker(this.loaderWorkers[i]); + } + + log.debug("Adding new InputWorker"); + this.inputWorker = new InputWorker(); + this.inputWorker.postMessage({ + action: "updateMaxWorkers", + data: this.maxWorkers + }); + this.inputWorker.postMessage({ + action: "updateMaxTabs", + data: { + maxTabs: this.maxTabs, + activeTab: this.manager.tabs.getActiveInputTab() + } + }); + this.inputWorker.postMessage({ + action: "setLogLevel", + data: log.getLevel() + }); + this.inputWorker.addEventListener("message", this.handleInputWorkerMessage.bind(this)); + } + + /** + * Activates a loaderWorker and sends it to the InputWorker + */ + activateLoaderWorker() { + const workerIdx = this.addLoaderWorker(); + if (workerIdx === -1) return; + + const workerObj = this.loaderWorkers[workerIdx]; + this.inputWorker.postMessage({ + action: "loaderWorkerReady", + data: { + id: workerObj.id + } + }); + } + + /** + * Adds a new loaderWorker + * + * @returns {number} - The index of the created worker + */ + addLoaderWorker() { + if (this.loaderWorkers.length === this.maxWorkers) { + return -1; + } + log.debug("Adding new LoaderWorker."); + const newWorker = new LoaderWorker(); + const workerId = this.workerId++; + newWorker.addEventListener("message", this.handleLoaderMessage.bind(this)); + newWorker.postMessage({id: workerId}); + const newWorkerObj = { + worker: newWorker, + id: workerId + }; + this.loaderWorkers.push(newWorkerObj); + return this.loaderWorkers.indexOf(newWorkerObj); + } + + /** + * Removes a loaderworker + * + * @param {Object} workerObj - Object containing the loaderWorker and its id + * @param {LoaderWorker} workerObj.worker - The actual loaderWorker + * @param {number} workerObj.id - The ID of the loaderWorker + */ + removeLoaderWorker(workerObj) { + const idx = this.loaderWorkers.indexOf(workerObj); + if (idx === -1) { + return; + } + log.debug(`Terminating worker ${this.loaderWorkers[idx].id}`); + this.loaderWorkers[idx].worker.terminate(); + this.loaderWorkers.splice(idx, 1); + } + + /** + * Finds and returns the object for the loaderWorker of a given id + * + * @param {number} id - The ID of the loaderWorker to find + * @returns {object} + */ + getLoaderWorker(id) { + const idx = this.getLoaderWorkerIndex(id); + if (idx === -1) return; + return this.loaderWorkers[idx]; + } + + /** + * Gets the index for the loaderWorker of a given id + * + * @param {number} id - The ID of hte loaderWorker to find + * @returns {number} The current index of the loaderWorker in the array + */ + getLoaderWorkerIndex(id) { + for (let i = 0; i < this.loaderWorkers.length; i++) { + if (this.loaderWorkers[i].id === id) { + return i; + } + } + return -1; + } + + /** + * Sends an input to be loaded to the loaderWorker + * + * @param {object} inputData - Object containing the input to be loaded + * @param {File} inputData.file - The actual file object to load + * @param {number} inputData.inputNum - The inputNum for the file object + * @param {number} inputData.workerId - The ID of the loaderWorker that will load it + */ + loadInput(inputData) { + const idx = this.getLoaderWorkerIndex(inputData.workerId); + if (idx === -1) return; + this.loaderWorkers[idx].worker.postMessage({ + file: inputData.file, + inputNum: inputData.inputNum + }); + } + + /** + * Handler for messages sent back by the loaderWorker + * Sends the message straight to the inputWorker to be handled there. + * + * @param {MessageEvent} e + */ + handleLoaderMessage(e) { + const r = e.data; + + if (Object.prototype.hasOwnProperty.call(r, "progress") && + Object.prototype.hasOwnProperty.call(r, "inputNum")) { + this.manager.tabs.updateInputTabProgress(r.inputNum, r.progress, 100); + } else if (Object.prototype.hasOwnProperty.call(r, "fileBuffer")) { + this.manager.tabs.updateInputTabProgress(r.inputNum, 100, 100); + } + + const transferable = Object.prototype.hasOwnProperty.call(r, "fileBuffer") ? [r.fileBuffer] : undefined; + this.inputWorker.postMessage({ + action: "loaderWorkerMessage", + data: r + }, transferable); + } + + + /** + * Handler for messages sent back by the inputWorker + * + * @param {MessageEvent} e + */ + handleInputWorkerMessage(e) { + const r = e.data; + + if (!("action" in r)) { + log.error("A message was received from the InputWorker with no action property. Ignoring message."); + return; + } + + log.debug(`Receiving ${r.action} from InputWorker.`); + + switch (r.action) { + case "activateLoaderWorker": + this.activateLoaderWorker(); + break; + case "loadInput": + this.loadInput(r.data); + break; + case "terminateLoaderWorker": + this.removeLoaderWorker(this.getLoaderWorker(r.data)); + break; + case "refreshTabs": + this.refreshTabs(r.data.nums, r.data.activeTab, r.data.tabsLeft, r.data.tabsRight); + break; + case "changeTab": + this.changeTab(r.data, this.app.options.syncTabs); + break; + case "updateTabHeader": + this.manager.tabs.updateInputTabHeader(r.data.inputNum, r.data.input); + break; + case "loadingInfo": + this.showLoadingInfo(r.data, true); + break; + case "setInput": + this.app.debounce(this.set, 50, "setInput", this, [r.data.inputObj, r.data.silent])(); + break; + case "inputAdded": + this.inputAdded(r.data.changeTab, r.data.inputNum); + break; + case "queueInput": + this.manager.worker.queueInput(r.data); + break; + case "queueInputError": + this.manager.worker.queueInputError(r.data); + break; + case "bakeAllInputs": + this.manager.worker.bakeAllInputs(r.data); + break; + case "displayTabSearchResults": + this.displayTabSearchResults(r.data); + break; + case "filterTabError": + this.app.handleError(r.data); + break; + case "setUrl": + this.setUrl(r.data); + break; + case "inputSwitch": + this.manager.output.inputSwitch(r.data); + break; + case "getInput": + case "getInputNums": + this.callbacks[r.data.id](r.data); + break; + case "removeChefWorker": + this.removeChefWorker(); + break; + default: + log.error(`Unknown action ${r.action}.`); + } + } + + /** + * Sends a message to the inputWorker to bake all inputs + */ + bakeAll() { + this.app.progress = 0; + this.app.debounce(this.manager.controls.toggleBakeButtonFunction, 20, "toggleBakeButton", this, ["loading"]); + this.inputWorker.postMessage({ + action: "bakeAll" + }); + } + + /** + * Sets the input in the input area + * + * @param {object} inputData - Object containing the input and its metadata + * @param {number} inputData.inputNum - The unique inputNum for the selected input + * @param {string | object} inputData.input - The actual input data + * @param {string} inputData.name - The name of the input file + * @param {number} inputData.size - The size in bytes of the input file + * @param {string} inputData.type - The MIME type of the input file + * @param {number} inputData.progress - The load progress of the input file + * @param {boolean} [silent=false] - If true, fires the manager statechange event + */ + async set(inputData, silent=false) { + return new Promise(function(resolve, reject) { + const activeTab = this.manager.tabs.getActiveInputTab(); + if (inputData.inputNum !== activeTab) return; + + const inputText = document.getElementById("input-text"); + + if (typeof inputData.input === "string") { + inputText.value = inputData.input; + const fileOverlay = document.getElementById("input-file"), + fileName = document.getElementById("input-file-name"), + fileSize = document.getElementById("input-file-size"), + fileType = document.getElementById("input-file-type"), + fileLoaded = document.getElementById("input-file-loaded"); + + fileOverlay.style.display = "none"; + fileName.textContent = ""; + fileSize.textContent = ""; + fileType.textContent = ""; + fileLoaded.textContent = ""; + + inputText.style.overflow = "auto"; + inputText.classList.remove("blur"); + inputText.scroll(0, 0); + + const lines = inputData.input.length < (this.app.options.ioDisplayThreshold * 1024) ? + inputData.input.count("\n") + 1 : null; + this.setInputInfo(inputData.input.length, lines); + + // Set URL to current input + const inputStr = toBase64(inputData.input, "A-Za-z0-9+/"); + if (inputStr.length > 0 && inputStr.length <= 68267) { + this.setUrl({ + includeInput: true, + input: inputStr + }); + } + + if (!silent) window.dispatchEvent(this.manager.statechange); + } else { + this.setFile(inputData); + } + + }.bind(this)); + } + + /** + * Displays file details + * + * @param {object} inputData - Object containing the input and its metadata + * @param {number} inputData.inputNum - The unique inputNum for the selected input + * @param {string | object} inputData.input - The actual input data + * @param {string} inputData.name - The name of the input file + * @param {number} inputData.size - The size in bytes of the input file + * @param {string} inputData.type - The MIME type of the input file + * @param {number} inputData.progress - The load progress of the input file + */ + setFile(inputData) { + const activeTab = this.manager.tabs.getActiveInputTab(); + if (inputData.inputNum !== activeTab) return; + + const fileOverlay = document.getElementById("input-file"), + fileName = document.getElementById("input-file-name"), + fileSize = document.getElementById("input-file-size"), + fileType = document.getElementById("input-file-type"), + fileLoaded = document.getElementById("input-file-loaded"); + + fileOverlay.style.display = "block"; + fileName.textContent = inputData.name; + fileSize.textContent = inputData.size + " bytes"; + fileType.textContent = inputData.type; + if (inputData.status === "error") { + fileLoaded.textContent = "Error"; + fileLoaded.style.color = "#FF0000"; + } else { + fileLoaded.style.color = ""; + fileLoaded.textContent = inputData.progress + "%"; + } + + this.setInputInfo(inputData.size, null); + this.displayFilePreview(inputData); + } + + /** + * Render the input thumbnail + */ + async renderFileThumb() { + const activeTab = this.manager.tabs.getActiveInputTab(), + input = await this.getInputValue(activeTab), + fileThumb = document.getElementById("input-file-thumbnail"); + + if (typeof input === "string" || + !this.app.options.imagePreview) { + this.resetFileThumb(); + return; + } + + const inputArr = new Uint8Array(input), + type = isImage(inputArr); + + if (type && type !== "image/tiff" && inputArr.byteLength <= 512000) { + // Most browsers don't support displaying TIFFs, so ignore them + // Don't render images over 512000 bytes + const blob = new Blob([inputArr], {type: type}), + url = URL.createObjectURL(blob); + fileThumb.src = url; + } else { + this.resetFileThumb(); + } + + } + + /** + * Reset the input thumbnail to the default icon + */ + resetFileThumb() { + const fileThumb = document.getElementById("input-file-thumbnail"); + fileThumb.src = require("../static/images/file-128x128.png"); + } + + /** + * Shows a chunk of the file in the input behind the file overlay + * + * @param {Object} inputData - Object containing the input data + * @param {number} inputData.inputNum - The inputNum of the file being displayed + * @param {ArrayBuffer} inputData.input - The actual input to display + */ + displayFilePreview(inputData) { + const activeTab = this.manager.tabs.getActiveInputTab(), + input = inputData.input, + inputText = document.getElementById("input-text"); + if (inputData.inputNum !== activeTab) return; + inputText.style.overflow = "hidden"; + inputText.classList.add("blur"); + inputText.value = Utils.printable(Utils.arrayBufferToStr(input.slice(0, 4096))); + + this.renderFileThumb(); + + } + + /** + * Updates the displayed load progress for a file + * + * @param {number} inputNum + * @param {number | string} progress - Either a number or "error" + */ + updateFileProgress(inputNum, progress) { + const activeTab = this.manager.tabs.getActiveInputTab(); + if (inputNum !== activeTab) return; + + const fileLoaded = document.getElementById("input-file-loaded"); + let oldProgress = fileLoaded.textContent; + if (oldProgress !== "Error") { + oldProgress = parseInt(oldProgress.replace("%", ""), 10); + } + if (progress === "error") { + fileLoaded.textContent = "Error"; + fileLoaded.style.color = "#FF0000"; + } else { + fileLoaded.textContent = progress + "%"; + fileLoaded.style.color = ""; + } + + if (progress === 100 && progress !== oldProgress) { + // Don't set the input if the progress hasn't changed + this.inputWorker.postMessage({ + action: "setInput", + data: { + inputNum: inputNum, + silent: false + } + }); + window.dispatchEvent(this.manager.statechange); + + } + } + + /** + * Updates the stored value for the specified inputNum + * + * @param {number} inputNum + * @param {string | ArrayBuffer} value + * @param {boolean} [force=false] - If true, forces the value to be updated even if the type is different to the currently stored type + */ + updateInputValue(inputNum, value, force=false) { + let includeInput = false; + const recipeStr = toBase64(value, "A-Za-z0-9+/"); // B64 alphabet with no padding + if (recipeStr.length > 0 && recipeStr.length <= 68267) { + includeInput = true; + } + this.setUrl({ + includeInput: includeInput, + input: recipeStr + }); + + // Value is either a string set by the input or an ArrayBuffer from a LoaderWorker, + // so is safe to use typeof === "string" + const transferable = (typeof value !== "string") ? [value] : undefined; + this.inputWorker.postMessage({ + action: "updateInputValue", + data: { + inputNum: inputNum, + value: value, + force: force + } + }, transferable); + } + + /** + * Updates the .data property for the input of the specified inputNum. + * Used for switching the output into the input + * + * @param {number} inputNum - The inputNum of the input we're changing + * @param {object} inputData - The new data object + */ + updateInputObj(inputNum, inputData) { + const transferable = (typeof inputData !== "string") ? [inputData.fileBuffer] : undefined; + this.inputWorker.postMessage({ + action: "updateInputObj", + data: { + inputNum: inputNum, + data: inputData + } + }, transferable); + } + + /** + * Get the input value for the specified input + * + * @param {number} inputNum - The inputNum of the input to retrieve from the inputWorker + * @returns {ArrayBuffer | string} + */ + async getInputValue(inputNum) { + return await new Promise(resolve => { + this.getInput(inputNum, false, r => { + resolve(r.data); + }); + }); + } + + /** + * Get the input object for the specified input + * + * @param {number} inputNum - The inputNum of the input to retrieve from the inputWorker + * @returns {object} + */ + async getInputObj(inputNum) { + return await new Promise(resolve => { + this.getInput(inputNum, true, r => { + resolve(r.data); + }); + }); + } + + /** + * Gets the specified input from the inputWorker + * + * @param {number} inputNum - The inputNum of the data to get + * @param {boolean} getObj - If true, get the actual data object of the input instead of just the value + * @param {Function} callback - The callback to execute when the input is returned + * @returns {ArrayBuffer | string | object} + */ + getInput(inputNum, getObj, callback) { + const id = this.callbackID++; + + this.callbacks[id] = callback; + + this.inputWorker.postMessage({ + action: "getInput", + data: { + inputNum: inputNum, + getObj: getObj, + id: id + } + }); + } + + /** + * Gets the number of inputs from the inputWorker + * + * @returns {object} + */ + async getInputNums() { + return await new Promise(resolve => { + this.getNums(r => { + resolve(r); + }); + }); + } + + /** + * Gets a list of inputNums from the inputWorker, and sends + * them back to the specified callback + */ + getNums(callback) { + const id = this.callbackID++; + + this.callbacks[id] = callback; + + this.inputWorker.postMessage({ + action: "getInputNums", + data: id + }); + } + + + /** + * Displays information about the input. + * + * @param {number} length - The length of the current input string + * @param {number} lines - The number of the lines in the current input string + */ + setInputInfo(length, lines) { + let width = length.toString().length.toLocaleString(); + width = width < 2 ? 2 : width; + + const lengthStr = length.toString().padStart(width, " ").replace(/ /g, " "); + let msg = "length: " + lengthStr; + + if (typeof lines === "number") { + const linesStr = lines.toString().padStart(width, " ").replace(/ /g, " "); + msg += "
    lines: " + linesStr; + } + + document.getElementById("input-info").innerHTML = msg; + + } + + /** + * Handler for input change events. + * Debounces the input so we don't call autobake too often. + * + * @param {event} e + */ + debounceInputChange(e) { + this.app.debounce(this.inputChange, 50, "inputChange", this, [e])(); + } + + /** + * Handler for input change events. + * Updates the value stored in the inputWorker + * + * @param {event} e + * + * @fires Manager#statechange + */ + inputChange(e) { + // Ignore this function if the input is a file + const fileOverlay = document.getElementById("input-file"); + if (fileOverlay.style.display === "block") return; + + // Remove highlighting from input and output panes as the offsets might be different now + this.manager.highlighter.removeHighlights(); + + const textArea = document.getElementById("input-text"); + const value = (textArea.value !== undefined) ? textArea.value : ""; + const activeTab = this.manager.tabs.getActiveInputTab(); + + this.app.progress = 0; + + const lines = value.length < (this.app.options.ioDisplayThreshold * 1024) ? + (value.count("\n") + 1) : null; + this.setInputInfo(value.length, lines); + this.updateInputValue(activeTab, value); + this.manager.tabs.updateInputTabHeader(activeTab, value.replace(/[\n\r]/g, "").slice(0, 100)); + + if (e && this.badKeys.indexOf(e.keyCode) < 0) { + // Fire the statechange event as the input has been modified + window.dispatchEvent(this.manager.statechange); + } + } + + /** + * Handler for input paste events + * Checks that the size of the input is below the display limit, otherwise treats it as a file/blob + * + * @param {event} e + */ + inputPaste(e) { + const pastedData = e.clipboardData.getData("Text"); + if (pastedData.length < (this.app.options.ioDisplayThreshold * 1024)) { + // Pasting normally fires the inputChange() event before + // changing the value, so instead change it here ourselves + // and manually fire inputChange() + e.preventDefault(); + const inputText = document.getElementById("input-text"); + const selStart = inputText.selectionStart; + const selEnd = inputText.selectionEnd; + const startVal = inputText.value.slice(0, selStart); + const endVal = inputText.value.slice(selEnd); + + inputText.value = startVal + pastedData + endVal; + inputText.setSelectionRange(selStart + pastedData.length, selStart + pastedData.length); + this.debounceInputChange(e); + } else { + e.preventDefault(); + e.stopPropagation(); + + const file = new File([pastedData], "PastedData", { + type: "text/plain", + lastModified: Date.now() + }); + + this.loadUIFiles([file]); + return false; + } + } + + + /** + * Handler for input dragover events. + * Gives the user a visual cue to show that items can be dropped here. + * + * @param {event} e + */ + inputDragover(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("#input-text,#input-file").classList.add("dropping-file"); + } + + /** + * Handler for input dragleave events. + * Removes the visual cue. + * + * @param {event} e + */ + inputDragleave(e) { + e.stopPropagation(); + e.preventDefault(); + e.target.closest("#input-text,#input-file").classList.remove("dropping-file"); + } + + /** + * Handler for input drop events. + * Loads the dragged data. + * + * @param {event} e + */ + inputDrop(e) { + // This will be set if we're dragging an operation + if (e.dataTransfer.effectAllowed === "move") + return false; + + e.stopPropagation(); + e.preventDefault(); + + const text = e.dataTransfer.getData("Text"); + + e.target.closest("#input-text,#input-file").classList.remove("dropping-file"); + + if (text) { + // Append the text to the current input and fire inputChange() + document.getElementById("input-text").value += text; + this.inputChange(e); + return; + } + + if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { + this.loadUIFiles(e.dataTransfer.files); + } + } + + /** + * Handler for open input button events + * Loads the opened data into the input textarea + * + * @param {event} e + */ + inputOpen(e) { + e.preventDefault(); + + if (e.target.files.length > 0) { + this.loadUIFiles(e.target.files); + e.target.value = ""; + } + } + + /** + * Load files from the UI into the inputWorker + * + * @param {FileList} files - The list of files to be loaded + */ + loadUIFiles(files) { + const numFiles = files.length; + const activeTab = this.manager.tabs.getActiveInputTab(); + log.debug(`Loading ${numFiles} files.`); + + // Display the number of files as pending so the user + // knows that we've received the files. + this.showLoadingInfo({ + pending: numFiles, + loading: 0, + loaded: 0, + total: numFiles, + activeProgress: { + inputNum: activeTab, + progress: 0 + } + }, false); + + this.inputWorker.postMessage({ + action: "loadUIFiles", + data: { + files: files, + activeTab: activeTab + } + }); + } + + /** + * Handler for open input button click. + * Opens the open file dialog. + */ + inputOpenClick() { + document.getElementById("open-file").click(); + } + + /** + * Handler for open folder button click + * Opens the open folder dialog. + */ + folderOpenClick() { + document.getElementById("open-folder").click(); + } + + /** + * Display the loaded files information in the input header. + * Also, sets the background of the Input header to be a progress bar + * @param {object} loadedData - Object containing the loading information + * @param {number} loadedData.pending - How many files are pending (not loading / loaded) + * @param {number} loadedData.loading - How many files are being loaded + * @param {number} loadedData.loaded - How many files have been loaded + * @param {number} loadedData.total - The total number of files + * @param {object} loadedData.activeProgress - Object containing data about the active tab + * @param {number} loadedData.activeProgress.inputNum - The inputNum of the input the progress is for + * @param {number} loadedData.activeProgress.progress - The loading progress of the active input + * @param {boolean} autoRefresh - If true, automatically refreshes the loading info by sending a message to the inputWorker after 100ms + */ + showLoadingInfo(loadedData, autoRefresh) { + const pending = loadedData.pending; + const loading = loadedData.loading; + const loaded = loadedData.loaded; + const total = loadedData.total; + + let width = total.toLocaleString().length; + width = width < 2 ? 2 : width; + + const totalStr = total.toLocaleString().padStart(width, " ").replace(/ /g, " "); + let msg = "total: " + totalStr; + + const loadedStr = loaded.toLocaleString().padStart(width, " ").replace(/ /g, " "); + msg += "
    loaded: " + loadedStr; + + if (pending > 0) { + const pendingStr = pending.toLocaleString().padStart(width, " ").replace(/ /g, " "); + msg += "
    pending: " + pendingStr; + } else if (loading > 0) { + const loadingStr = loading.toLocaleString().padStart(width, " ").replace(/ /g, " "); + msg += "
    loading: " + loadingStr; + } + + const inFiles = document.getElementById("input-files-info"); + if (total > 1) { + inFiles.innerHTML = msg; + inFiles.style.display = ""; + } else { + inFiles.style.display = "none"; + } + + this.updateFileProgress(loadedData.activeProgress.inputNum, loadedData.activeProgress.progress); + + const inputTitle = document.getElementById("input").firstElementChild; + if (loaded < total) { + const percentComplete = loaded / total * 100; + inputTitle.style.background = `linear-gradient(to right, var(--title-background-colour) ${percentComplete}%, var(--primary-background-colour) ${percentComplete}%)`; + } else { + inputTitle.style.background = ""; + } + + if (loaded < total && autoRefresh) { + setTimeout(function() { + this.inputWorker.postMessage({ + action: "getLoadProgress", + data: this.manager.tabs.getActiveInputTab() + }); + }.bind(this), 100); + } + } + + /** + * Change to a different tab. + * + * @param {number} inputNum - The inputNum of the tab to change to + * @param {boolean} [changeOutput=false] - If true, also changes the output + */ + changeTab(inputNum, changeOutput) { + if (this.manager.tabs.getInputTabItem(inputNum) !== null) { + this.manager.tabs.changeInputTab(inputNum); + this.inputWorker.postMessage({ + action: "setInput", + data: { + inputNum: inputNum, + silent: true + } + }); + } else { + const minNum = Math.min(...this.manager.tabs.getInputTabList()); + let direction = "right"; + if (inputNum < minNum) { + direction = "left"; + } + this.inputWorker.postMessage({ + action: "refreshTabs", + data: { + inputNum: inputNum, + direction: direction + } + }); + } + + if (changeOutput) { + this.manager.output.changeTab(inputNum, false); + } + } + + /** + * Handler for clicking on a tab + * + * @param {event} mouseEvent + */ + changeTabClick(mouseEvent) { + if (!mouseEvent.target) return; + + const tabNum = mouseEvent.target.parentElement.getAttribute("inputNum"); + if (tabNum >= 0) { + this.changeTab(parseInt(tabNum, 10), this.app.options.syncTabs); + } + } + + /** + * Handler for clear all IO events. + * Resets the input, output and info areas, and creates a new inputWorker + */ + clearAllIoClick() { + this.manager.worker.cancelBake(true, true); + this.manager.worker.loaded = false; + this.manager.output.removeAllOutputs(); + this.manager.output.terminateZipWorker(); + + this.manager.highlighter.removeHighlights(); + getSelection().removeAllRanges(); + + const tabsList = document.getElementById("input-tabs"); + const tabsListChildren = tabsList.children; + + tabsList.classList.remove("tabs-left"); + tabsList.classList.remove("tabs-right"); + for (let i = tabsListChildren.length - 1; i >= 0; i--) { + tabsListChildren.item(i).remove(); + } + + this.showLoadingInfo({ + pending: 0, + loading: 0, + loaded: 1, + total: 1, + activeProgress: { + inputNum: 1, + progress: 100 + } + }); + + this.setupInputWorker(); + this.manager.worker.setupChefWorker(); + this.addInput(true); + this.bakeAll(); + } + + /** + * Handler for clear IO click event. + * Resets the input for the current tab + */ + clearIoClick() { + const inputNum = this.manager.tabs.getActiveInputTab(); + if (inputNum === -1) return; + + this.manager.highlighter.removeHighlights(); + getSelection().removeAllRanges(); + + this.updateInputValue(inputNum, "", true); + + this.set({ + inputNum: inputNum, + input: "" + }); + + this.manager.tabs.updateInputTabHeader(inputNum, ""); + } + + /** + * Sets the console log level in the worker. + * + * @param {string} level + */ + setLogLevel(level) { + if (!this.inputWorker) return; + this.inputWorker.postMessage({ + action: "setLogLevel", + data: log.getLevel() + }); + } + + /** + * Sends a message to the inputWorker to add a new input. + * @param {boolean} [changeTab=false] - If true, changes the tab to the new input + */ + addInput(changeTab=false) { + if (!this.inputWorker) return; + this.inputWorker.postMessage({ + action: "addInput", + data: changeTab + }); + } + + /** + * Handler for add input button clicked. + */ + addInputClick() { + this.addInput(true); + } + + /** + * Handler for when the inputWorker adds a new input + * + * @param {boolean} changeTab - Whether or not to change to the new input tab + * @param {number} inputNum - The new inputNum + */ + inputAdded(changeTab, inputNum) { + this.addTab(inputNum, changeTab); + + this.manager.output.addOutput(inputNum, changeTab); + this.manager.worker.addChefWorker(); + } + + /** + * Remove a chefWorker from the workerWaiter if we remove an input + */ + removeChefWorker() { + const workerIdx = this.manager.worker.getInactiveChefWorker(true); + const worker = this.manager.worker.chefWorkers[workerIdx]; + this.manager.worker.removeChefWorker(worker); + } + + /** + * Adds a new input tab. + * + * @param {number} inputNum - The inputNum of the new tab + * @param {boolean} [changeTab=true] - If true, changes to the new tab once it's been added + */ + addTab(inputNum, changeTab = true) { + const tabsWrapper = document.getElementById("input-tabs"), + numTabs = tabsWrapper.children.length; + + if (!this.manager.tabs.getInputTabItem(inputNum) && numTabs < this.maxTabs) { + const newTab = this.manager.tabs.createInputTabElement(inputNum, changeTab); + tabsWrapper.appendChild(newTab); + + if (numTabs > 0) { + this.manager.tabs.showTabBar(); + } else { + this.manager.tabs.hideTabBar(); + } + + this.inputWorker.postMessage({ + action: "updateTabHeader", + data: inputNum + }); + } else if (numTabs === this.maxTabs) { + // Can't create a new tab + document.getElementById("input-tabs").lastElementChild.classList.add("tabs-right"); + } + + if (changeTab) this.changeTab(inputNum, false); + } + + /** + * Refreshes the input tabs, and changes to activeTab + * + * @param {number[]} nums - The inputNums to be displayed as tabs + * @param {number} activeTab - The tab to change to + * @param {boolean} tabsLeft - True if there are input tabs to the left of the displayed tabs + * @param {boolean} tabsRight - True if there are input tabs to the right of the displayed tabs + */ + refreshTabs(nums, activeTab, tabsLeft, tabsRight) { + this.manager.tabs.refreshInputTabs(nums, activeTab, tabsLeft, tabsRight); + + this.inputWorker.postMessage({ + action: "setInput", + data: { + inputNum: activeTab, + silent: true + } + }); + } + + /** + * Sends a message to the inputWorker to remove an input. + * If the input tab is on the screen, refreshes the tabs + * + * @param {number} inputNum - The inputNum of the tab to be removed + */ + removeInput(inputNum) { + let refresh = false; + if (this.manager.tabs.getInputTabItem(inputNum) !== null) { + refresh = true; + } + this.inputWorker.postMessage({ + action: "removeInput", + data: { + inputNum: inputNum, + refreshTabs: refresh, + removeChefWorker: true + } + }); + + this.manager.output.removeTab(inputNum); + } + + /** + * Handler for clicking on a remove tab button + * + * @param {event} mouseEvent + */ + removeTabClick(mouseEvent) { + if (!mouseEvent.target) { + return; + } + const tabNum = mouseEvent.target.closest("button").parentElement.getAttribute("inputNum"); + if (tabNum) { + this.removeInput(parseInt(tabNum, 10)); + } + } + + /** + * Handler for scrolling on the input tabs area + * + * @param {event} wheelEvent + */ + scrollTab(wheelEvent) { + wheelEvent.preventDefault(); + + if (wheelEvent.deltaY > 0) { + this.changeTabLeft(); + } else if (wheelEvent.deltaY < 0) { + this.changeTabRight(); + } + } + + /** + * Handler for mouse down on the next tab button + */ + nextTabClick() { + this.mousedown = true; + this.changeTabRight(); + const time = 200; + const func = function(time) { + if (this.mousedown) { + this.changeTabRight(); + const newTime = (time > 50) ? time = time - 10 : 50; + setTimeout(func.bind(this, [newTime]), newTime); + } + }; + this.tabTimeout = setTimeout(func.bind(this, [time]), time); + } + + /** + * Handler for mouse down on the previous tab button + */ + previousTabClick() { + this.mousedown = true; + this.changeTabLeft(); + const time = 200; + const func = function(time) { + if (this.mousedown) { + this.changeTabLeft(); + const newTime = (time > 50) ? time = time - 10 : 50; + setTimeout(func.bind(this, [newTime]), newTime); + } + }; + this.tabTimeout = setTimeout(func.bind(this, [time]), time); + } + + /** + * Handler for mouse up event on the tab buttons + */ + tabMouseUp() { + this.mousedown = false; + + clearTimeout(this.tabTimeout); + this.tabTimeout = null; + } + + /** + * Changes to the next (right) tab + */ + changeTabRight() { + const activeTab = this.manager.tabs.getActiveInputTab(); + if (activeTab === -1) return; + this.inputWorker.postMessage({ + action: "changeTabRight", + data: { + activeTab: activeTab + } + }); + } + + /** + * Changes to the previous (left) tab + */ + changeTabLeft() { + const activeTab = this.manager.tabs.getActiveInputTab(); + if (activeTab === -1) return; + this.inputWorker.postMessage({ + action: "changeTabLeft", + data: { + activeTab: activeTab + } + }); + } + + /** + * Handler for go to tab button clicked + */ + async goToTab() { + const inputNums = await this.getInputNums(); + let tabNum = window.prompt(`Enter tab number (${inputNums.min} - ${inputNums.max}):`, this.manager.tabs.getActiveInputTab().toString()); + + if (tabNum === null) return; + tabNum = parseInt(tabNum, 10); + + this.changeTab(tabNum, this.app.options.syncTabs); + } + + /** + * Handler for find tab button clicked + */ + findTab() { + this.filterTabSearch(); + $("#input-tab-modal").modal(); + } + + /** + * Sends a message to the inputWorker to search the inputs + */ + filterTabSearch() { + const showPending = document.getElementById("input-show-pending").checked; + const showLoading = document.getElementById("input-show-loading").checked; + const showLoaded = document.getElementById("input-show-loaded").checked; + + const filter = document.getElementById("input-filter").value; + const filterType = document.getElementById("input-filter-button").innerText; + const numResults = parseInt(document.getElementById("input-num-results").value, 10); + + this.inputWorker.postMessage({ + action: "filterTabs", + data: { + showPending: showPending, + showLoading: showLoading, + showLoaded: showLoaded, + filter: filter, + filterType: filterType, + numResults: numResults + } + }); + } + + /** + * Handle when an option in the filter drop down box is clicked + * + * @param {event} mouseEvent + */ + filterOptionClick(mouseEvent) { + document.getElementById("input-filter-button").innerText = mouseEvent.target.innerText; + this.filterTabSearch(); + } + + /** + * Displays the results of a tab search in the find tab box + * + * @param {object[]} results - List of results objects + * + */ + displayTabSearchResults(results) { + const resultsList = document.getElementById("input-search-results"); + + for (let i = resultsList.children.length - 1; i >= 0; i--) { + resultsList.children.item(i).remove(); + } + + for (let i = 0; i < results.length; i++) { + const newListItem = document.createElement("li"); + newListItem.classList.add("input-filter-result"); + newListItem.setAttribute("inputNum", results[i].inputNum); + newListItem.innerText = `${results[i].inputNum}: ${results[i].textDisplay}`; + + resultsList.appendChild(newListItem); + } + } + + /** + * Handler for clicking on a filter result + * + * @param {event} e + */ + filterItemClick(e) { + if (!e.target) return; + const inputNum = parseInt(e.target.getAttribute("inputNum"), 10); + if (inputNum <= 0) return; + + $("#input-tab-modal").modal("hide"); + this.changeTab(inputNum, this.app.options.syncTabs); + } + + /** + * Update the input URL to the new value + * + * @param {object} urlData - Object containing the URL data + * @param {boolean} urlData.includeInput - If true, the input is included in the title + * @param {string} urlData.input - The input data to be included + */ + setUrl(urlData) { + this.app.updateTitle(urlData.includeInput, urlData.input, true); + } + + +} + +export default InputWaiter; diff --git a/src/web/OperationsWaiter.mjs b/src/web/waiters/OperationsWaiter.mjs similarity index 99% rename from src/web/OperationsWaiter.mjs rename to src/web/waiters/OperationsWaiter.mjs index decc49d6..19adba9f 100755 --- a/src/web/OperationsWaiter.mjs +++ b/src/web/waiters/OperationsWaiter.mjs @@ -4,7 +4,7 @@ * @license Apache-2.0 */ -import HTMLOperation from "./HTMLOperation"; +import HTMLOperation from "../HTMLOperation.mjs"; import Sortable from "sortablejs"; diff --git a/src/web/OptionsWaiter.mjs b/src/web/waiters/OptionsWaiter.mjs similarity index 99% rename from src/web/OptionsWaiter.mjs rename to src/web/waiters/OptionsWaiter.mjs index 3f08b91b..eb6bac18 100755 --- a/src/web/OptionsWaiter.mjs +++ b/src/web/waiters/OptionsWaiter.mjs @@ -168,6 +168,7 @@ OptionsWaiter.prototype.logLevelChange = function (e) { const level = e.target.value; log.setLevel(level, false); this.manager.worker.setLogLevel(); + this.manager.input.setLogLevel(); }; export default OptionsWaiter; diff --git a/src/web/waiters/OutputWaiter.mjs b/src/web/waiters/OutputWaiter.mjs new file mode 100755 index 00000000..005d9533 --- /dev/null +++ b/src/web/waiters/OutputWaiter.mjs @@ -0,0 +1,1406 @@ +/** + * @author n1474335 [n1474335@gmail.com] + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2016 + * @license Apache-2.0 + */ + +import Utils from "../../core/Utils.mjs"; +import Dish from "../../core/Dish.mjs"; +import FileSaver from "file-saver"; +import ZipWorker from "worker-loader?inline&fallback=false!../workers/ZipWorker"; + +/** + * Waiter to handle events related to the output + */ +class OutputWaiter { + + /** + * OutputWaiter constructor. + * + * @param {App} app - The main view object for CyberChef. + * @param {Manager} manager - The CyberChef event manager + */ + constructor(app, manager) { + this.app = app; + this.manager = manager; + + this.outputs = {}; + this.zipWorker = null; + this.maxTabs = this.manager.tabs.calcMaxTabs(); + this.tabTimeout = null; + } + + /** + * Calculates the maximum number of tabs to display + */ + calcMaxTabs() { + const numTabs = this.manager.tabs.calcMaxTabs(); + if (numTabs !== this.maxTabs) { + this.maxTabs = numTabs; + this.refreshTabs(this.manager.tabs.getActiveOutputTab(), "right"); + } + } + + /** + * Gets the dish object for an output. + * + * @param inputNum - The inputNum of the output to get the dish of + * @returns {Dish} + */ + getOutputDish(inputNum) { + if (this.outputExists(inputNum) && + this.outputs[inputNum].data && + this.outputs[inputNum].data.dish) { + return this.outputs[inputNum].data.dish; + } + return null; + } + + /** + * Checks if an output exists in the output dictionary + * + * @param {number} inputNum - The number of the output we're looking for + * @returns {boolean} + */ + outputExists(inputNum) { + if (this.outputs[inputNum] === undefined || + this.outputs[inputNum] === null) { + return false; + } + return true; + } + + /** + * Adds a new output to the output array. + * Creates a new tab if we have less than maxtabs tabs open + * + * @param {number} inputNum - The inputNum of the new output + * @param {boolean} [changeTab=true] - If true, change to the new output + */ + addOutput(inputNum, changeTab = true) { + // Remove the output (will only get removed if it already exists) + this.removeOutput(inputNum); + + const newOutput = { + data: null, + inputNum: inputNum, + statusMessage: `Input ${inputNum} has not been baked yet.`, + error: null, + status: "inactive", + bakeId: -1, + progress: false + }; + + this.outputs[inputNum] = newOutput; + + this.addTab(inputNum, changeTab); + } + + /** + * Updates the value for the output in the output array. + * If this is the active output tab, updates the output textarea + * + * @param {ArrayBuffer | String} data + * @param {number} inputNum + * @param {boolean} set + */ + updateOutputValue(data, inputNum, set=true) { + if (!this.outputExists(inputNum)) { + this.addOutput(inputNum); + } + + if (Object.prototype.hasOwnProperty.call(data, "dish")) { + data.dish = new Dish(data.dish); + } + + this.outputs[inputNum].data = data; + + const tabItem = this.manager.tabs.getOutputTabItem(inputNum); + if (tabItem) tabItem.style.background = ""; + + if (set) this.set(inputNum); + } + + /** + * Updates the status message for the output in the output array. + * If this is the active output tab, updates the output textarea + * + * @param {string} statusMessage + * @param {number} inputNum + * @param {boolean} [set=true] + */ + updateOutputMessage(statusMessage, inputNum, set=true) { + if (!this.outputExists(inputNum)) return; + this.outputs[inputNum].statusMessage = statusMessage; + if (set) this.set(inputNum); + } + + /** + * Updates the error value for the output in the output array. + * If this is the active output tab, calls app.handleError. + * Otherwise, the error will be handled when the output is switched to + * + * @param {Error} error + * @param {number} inputNum + * @param {number} [progress=0] + */ + updateOutputError(error, inputNum, progress=0) { + if (!this.outputExists(inputNum)) return; + + const errorString = error.displayStr || error.toString(); + + this.outputs[inputNum].error = errorString; + this.outputs[inputNum].progress = progress; + this.updateOutputStatus("error", inputNum); + } + + /** + * Updates the status value for the output in the output array + * + * @param {string} status + * @param {number} inputNum + */ + updateOutputStatus(status, inputNum) { + if (!this.outputExists(inputNum)) return; + this.outputs[inputNum].status = status; + + if (status !== "error") { + delete this.outputs[inputNum].error; + } + + this.displayTabInfo(inputNum); + this.set(inputNum); + } + + /** + * Updates the stored bake ID for the output in the ouptut array + * + * @param {number} bakeId + * @param {number} inputNum + */ + updateOutputBakeId(bakeId, inputNum) { + if (!this.outputExists(inputNum)) return; + this.outputs[inputNum].bakeId = bakeId; + } + + /** + * Updates the stored progress value for the output in the output array + * + * @param {number} progress + * @param {number} total + * @param {number} inputNum + */ + updateOutputProgress(progress, total, inputNum) { + if (!this.outputExists(inputNum)) return; + this.outputs[inputNum].progress = progress; + + if (progress !== false) { + this.manager.tabs.updateOutputTabProgress(inputNum, progress, total); + } + + } + + /** + * Removes an output from the output array. + * + * @param {number} inputNum + */ + removeOutput(inputNum) { + if (!this.outputExists(inputNum)) return; + + delete (this.outputs[inputNum]); + } + + /** + * Removes all output tabs + */ + removeAllOutputs() { + this.outputs = {}; + const tabsList = document.getElementById("output-tabs"); + const tabsListChildren = tabsList.children; + + tabsList.classList.remove("tabs-left"); + tabsList.classList.remove("tabs-right"); + for (let i = tabsListChildren.length - 1; i >= 0; i--) { + tabsListChildren.item(i).remove(); + } + } + + /** + * Sets the output in the output textarea. + * + * @param {number} inputNum + */ + async set(inputNum) { + if (inputNum !== this.manager.tabs.getActiveOutputTab() || + !this.outputExists(inputNum)) return; + this.toggleLoader(true); + + return new Promise(async function(resolve, reject) { + const output = this.outputs[inputNum], + activeTab = this.manager.tabs.getActiveOutputTab(); + if (typeof inputNum !== "number") inputNum = parseInt(inputNum, 10); + + const outputText = document.getElementById("output-text"); + const outputHtml = document.getElementById("output-html"); + const outputFile = document.getElementById("output-file"); + const outputHighlighter = document.getElementById("output-highlighter"); + const inputHighlighter = document.getElementById("input-highlighter"); + + // If pending or baking, show loader and status message + // If error, style the tab and handle the error + // If done, display the output if it's the active tab + // If inactive, show the last bake value (or blank) + if (output.status === "inactive" || + output.status === "stale" || + (output.status === "baked" && output.bakeId < this.manager.worker.bakeId)) { + this.manager.controls.showStaleIndicator(); + } else { + this.manager.controls.hideStaleIndicator(); + } + + if (output.progress !== undefined && !this.app.baking) { + this.manager.recipe.updateBreakpointIndicator(output.progress); + } else { + this.manager.recipe.updateBreakpointIndicator(false); + } + + document.getElementById("show-file-overlay").style.display = "none"; + + if (output.status === "pending" || output.status === "baking") { + // show the loader and the status message if it's being shown + // otherwise don't do anything + document.querySelector("#output-loader .loading-msg").textContent = output.statusMessage; + } else if (output.status === "error") { + // style the tab if it's being shown + this.toggleLoader(false); + outputText.style.display = "block"; + outputText.classList.remove("blur"); + outputHtml.style.display = "none"; + outputFile.style.display = "none"; + outputHighlighter.display = "none"; + inputHighlighter.display = "none"; + + if (output.error) { + outputText.value = output.error; + } else { + outputText.value = output.data.result; + } + outputHtml.innerHTML = ""; + } else if (output.status === "baked" || output.status === "inactive") { + document.querySelector("#output-loader .loading-msg").textContent = `Loading output ${inputNum}`; + this.closeFile(); + let scriptElements, lines, length; + + if (output.data === null) { + outputText.style.display = "block"; + outputHtml.style.display = "none"; + outputFile.style.display = "none"; + outputHighlighter.display = "block"; + inputHighlighter.display = "block"; + + outputText.value = ""; + outputHtml.innerHTML = ""; + + lines = 0; + length = 0; + this.toggleLoader(false); + return; + } + + switch (output.data.type) { + case "html": + outputText.style.display = "none"; + outputHtml.style.display = "block"; + outputFile.style.display = "none"; + outputHighlighter.style.display = "none"; + inputHighlighter.style.display = "none"; + + outputText.value = ""; + outputHtml.innerHTML = output.data.result; + + // Execute script sections + scriptElements = outputHtml.querySelectorAll("script"); + for (let i = 0; i < scriptElements.length; i++) { + try { + eval(scriptElements[i].innerHTML); // eslint-disable-line no-eval + } catch (err) { + log.error(err); + } + } + break; + case "ArrayBuffer": + outputText.style.display = "block"; + outputHtml.style.display = "none"; + outputHighlighter.display = "none"; + inputHighlighter.display = "none"; + + outputText.value = ""; + outputHtml.innerHTML = ""; + + length = output.data.result.byteLength; + this.setFile(await this.getDishBuffer(output.data.dish), activeTab); + break; + case "string": + default: + outputText.style.display = "block"; + outputHtml.style.display = "none"; + outputFile.style.display = "none"; + outputHighlighter.display = "block"; + inputHighlighter.display = "block"; + + outputText.value = Utils.printable(output.data.result, true); + outputHtml.innerHTML = ""; + + lines = output.data.result.count("\n") + 1; + length = output.data.result.length; + break; + } + this.toggleLoader(false); + + if (output.data.type === "html") { + const dishStr = await this.getDishStr(output.data.dish); + length = dishStr.length; + lines = dishStr.count("\n") + 1; + } + + this.setOutputInfo(length, lines, output.data.duration); + this.backgroundMagic(); + } + }.bind(this)); + } + + /** + * Shows file details + * + * @param {ArrayBuffer} buf + * @param {number} activeTab + */ + setFile(buf, activeTab) { + if (activeTab !== this.manager.tabs.getActiveOutputTab()) return; + // Display file overlay in output area with details + const fileOverlay = document.getElementById("output-file"), + fileSize = document.getElementById("output-file-size"), + outputText = document.getElementById("output-text"), + fileSlice = buf.slice(0, 4096); + + fileOverlay.style.display = "block"; + fileSize.textContent = buf.byteLength.toLocaleString() + " bytes"; + + outputText.classList.add("blur"); + outputText.value = Utils.printable(Utils.arrayBufferToStr(fileSlice)); + } + + /** + * Clears output file details + */ + closeFile() { + document.getElementById("output-file").style.display = "none"; + document.getElementById("output-text").classList.remove("blur"); + } + + /** + * Retrieves the dish as a string, returning the cached version if possible. + * + * @param {Dish} dish + * @returns {string} + */ + async getDishStr(dish) { + return await new Promise(resolve => { + this.manager.worker.getDishAs(dish, "string", r => { + resolve(r.value); + }); + }); + } + + /** + * Retrieves the dish as an ArrayBuffer, returning the cached version if possible. + * + * @param {Dish} dish + * @returns {ArrayBuffer} + */ + async getDishBuffer(dish) { + return await new Promise(resolve => { + this.manager.worker.getDishAs(dish, "ArrayBuffer", r => { + resolve(r.value); + }); + }); + } + + /** + * Retrieves the title of the Dish as a string + * + * @param {Dish} dish + * @param {number} maxLength + * @returns {string} + */ + async getDishTitle(dish, maxLength) { + return await new Promise(resolve => { + this.manager.worker.getDishTitle(dish, maxLength, r => { + resolve(r.value); + }); + }); + } + + /** + * Save bombe object then remove it from the DOM so that it does not cause performance issues. + */ + saveBombe() { + this.bombeEl = document.getElementById("bombe"); + this.bombeEl.parentNode.removeChild(this.bombeEl); + } + + /** + * Shows or hides the output loading screen. + * The animated Bombe SVG, whilst quite aesthetically pleasing, is reasonably CPU + * intensive, so we remove it from the DOM when not in use. We only show it if the + * recipe is taking longer than 200ms. We add it to the DOM just before that so that + * it is ready to fade in without stuttering. + * + * @param {boolean} value - If true, show the loader + */ + toggleLoader(value) { + clearTimeout(this.appendBombeTimeout); + clearTimeout(this.outputLoaderTimeout); + + const outputLoader = document.getElementById("output-loader"), + outputElement = document.getElementById("output-text"), + animation = document.getElementById("output-loader-animation"); + + if (value) { + this.manager.controls.hideStaleIndicator(); + + // Don't add the bombe if it's already there! + if (animation.children.length > 0) return; + + // Start a timer to add the Bombe to the DOM just before we make it + // visible so that there is no stuttering + this.appendBombeTimeout = setTimeout(function() { + animation.appendChild(this.bombeEl); + }.bind(this), 150); + + // Show the loading screen + this.outputLoaderTimeout = setTimeout(function() { + outputElement.disabled = true; + outputLoader.style.visibility = "visible"; + outputLoader.style.opacity = 1; + }, 200); + } else { + // Remove the Bombe from the DOM to save resources + this.outputLoaderTimeout = setTimeout(function () { + try { + animation.removeChild(this.bombeEl); + } catch (err) {} + }.bind(this), 500); + outputElement.disabled = false; + outputLoader.style.opacity = 0; + outputLoader.style.visibility = "hidden"; + } + } + + /** + * Handler for save click events. + * Saves the current output to a file. + */ + saveClick() { + this.downloadFile(); + } + + /** + * Handler for file download events. + */ + async downloadFile() { + const dish = this.getOutputDish(this.manager.tabs.getActiveOutputTab()); + if (dish === null) { + this.app.alert("Could not find any output data to download. Has this output been baked?", 3000); + return; + } + let fileName = window.prompt("Please enter a filename: ", "download.dat"); + + if (fileName === null) fileName = "download.dat"; + + const data = await dish.get(Dish.ARRAY_BUFFER), + file = new File([data], fileName); + FileSaver.saveAs(file, fileName, false); + } + + /** + * Handler for save all click event + * Saves all outputs to a single archvie file + */ + saveAllClick() { + const downloadButton = document.getElementById("save-all-to-file"); + if (downloadButton.firstElementChild.innerHTML === "archive") { + this.downloadAllFiles(); + } else if (window.confirm("Cancel zipping of outputs?")) { + this.terminateZipWorker(); + } + } + + + /** + * Spawns a new ZipWorker and sends it the outputs so that they can + * be zipped for download + */ + async downloadAllFiles() { + return new Promise(resolve => { + const inputNums = Object.keys(this.outputs); + for (let i = 0; i < inputNums.length; i++) { + const iNum = inputNums[i]; + if (this.outputs[iNum].status !== "baked" || + this.outputs[iNum].bakeId !== this.manager.worker.bakeId) { + if (window.confirm("Not all outputs have been baked yet. Continue downloading outputs?")) { + break; + } else { + return; + } + } + } + + let fileName = window.prompt("Please enter a filename: ", "download.zip"); + + if (fileName === null || fileName === "") { + // Don't zip the files if there isn't a filename + this.app.alert("No filename was specified.", 3000); + return; + } + + if (!fileName.match(/.zip$/)) { + fileName += ".zip"; + } + + let fileExt = window.prompt("Please enter a file extension for the files, or leave blank to detect automatically.", ""); + + if (fileExt === null) fileExt = ""; + + if (this.zipWorker !== null) { + this.terminateZipWorker(); + } + + const downloadButton = document.getElementById("save-all-to-file"); + + downloadButton.classList.add("spin"); + downloadButton.title = `Zipping ${inputNums.length} files...`; + downloadButton.setAttribute("data-original-title", `Zipping ${inputNums.length} files...`); + + downloadButton.firstElementChild.innerHTML = "autorenew"; + + log.debug("Creating ZipWorker"); + this.zipWorker = new ZipWorker(); + this.zipWorker.postMessage({ + outputs: this.outputs, + filename: fileName, + fileExtension: fileExt + }); + this.zipWorker.addEventListener("message", this.handleZipWorkerMessage.bind(this)); + }); + } + + /** + * Terminate the ZipWorker + */ + terminateZipWorker() { + if (this.zipWorker === null) return; // Already terminated + + log.debug("Terminating ZipWorker."); + + this.zipWorker.terminate(); + this.zipWorker = null; + + const downloadButton = document.getElementById("save-all-to-file"); + + downloadButton.classList.remove("spin"); + downloadButton.title = "Save all outputs to a zip file"; + downloadButton.setAttribute("data-original-title", "Save all outputs to a zip file"); + + downloadButton.firstElementChild.innerHTML = "archive"; + + } + + + /** + * Handle messages sent back by the ZipWorker + */ + handleZipWorkerMessage(e) { + const r = e.data; + if (!("zippedFile" in r)) { + log.error("No zipped file was sent in the message."); + this.terminateZipWorker(); + return; + } + if (!("filename" in r)) { + log.error("No filename was sent in the message."); + this.terminateZipWorker(); + return; + } + + const file = new File([r.zippedFile], r.filename); + FileSaver.saveAs(file, r.filename, false); + + this.terminateZipWorker(); + } + + /** + * Adds a new output tab. + * + * @param {number} inputNum + * @param {boolean} [changeTab=true] + */ + addTab(inputNum, changeTab = true) { + const tabsWrapper = document.getElementById("output-tabs"); + const numTabs = tabsWrapper.children.length; + + if (!this.manager.tabs.getOutputTabItem(inputNum) && numTabs < this.maxTabs) { + // Create a new tab element + const newTab = this.manager.tabs.createOutputTabElement(inputNum, changeTab); + tabsWrapper.appendChild(newTab); + } else if (numTabs === this.maxTabs) { + // Can't create a new tab + document.getElementById("output-tabs").lastElementChild.classList.add("tabs-right"); + } + + this.displayTabInfo(inputNum); + + if (changeTab) { + this.changeTab(inputNum, false); + } + } + + /** + * Changes the active tab + * + * @param {number} inputNum + * @param {boolean} [changeInput = false] + */ + changeTab(inputNum, changeInput = false) { + if (!this.outputExists(inputNum)) return; + const currentNum = this.manager.tabs.getActiveOutputTab(); + + this.hideMagicButton(); + + this.manager.highlighter.removeHighlights(); + getSelection().removeAllRanges(); + + if (!this.manager.tabs.changeOutputTab(inputNum)) { + let direction = "right"; + if (currentNum > inputNum) { + direction = "left"; + } + const newOutputs = this.getNearbyNums(inputNum, direction); + + const tabsLeft = (newOutputs[0] !== this.getSmallestInputNum()); + const tabsRight = (newOutputs[newOutputs.length - 1] !== this.getLargestInputNum()); + + this.manager.tabs.refreshOutputTabs(newOutputs, inputNum, tabsLeft, tabsRight); + + for (let i = 0; i < newOutputs.length; i++) { + this.displayTabInfo(newOutputs[i]); + } + } + + this.app.debounce(this.set, 50, "setOutput", this, [inputNum])(); + + document.getElementById("output-html").scroll(0, 0); + document.getElementById("output-text").scroll(0, 0); + + if (changeInput) { + this.manager.input.changeTab(inputNum, false); + } + } + + /** + * Handler for changing tabs event + * + * @param {event} mouseEvent + */ + changeTabClick(mouseEvent) { + if (!mouseEvent.target) return; + const tabNum = mouseEvent.target.parentElement.getAttribute("inputNum"); + if (tabNum) { + this.changeTab(parseInt(tabNum, 10), this.app.options.syncTabs); + } + } + + /** + * Handler for scrolling on the output tabs area + * + * @param {event} wheelEvent + */ + scrollTab(wheelEvent) { + wheelEvent.preventDefault(); + + if (wheelEvent.deltaY > 0) { + this.changeTabLeft(); + } else if (wheelEvent.deltaY < 0) { + this.changeTabRight(); + } + } + + /** + * Handler for mouse down on the next tab button + */ + nextTabClick() { + this.mousedown = true; + this.changeTabRight(); + const time = 200; + const func = function(time) { + if (this.mousedown) { + this.changeTabRight(); + const newTime = (time > 50) ? time = time - 10 : 50; + setTimeout(func.bind(this, [newTime]), newTime); + } + }; + this.tabTimeout = setTimeout(func.bind(this, [time]), time); + } + + /** + * Handler for mouse down on the previous tab button + */ + previousTabClick() { + this.mousedown = true; + this.changeTabLeft(); + const time = 200; + const func = function(time) { + if (this.mousedown) { + this.changeTabLeft(); + const newTime = (time > 50) ? time = time - 10 : 50; + setTimeout(func.bind(this, [newTime]), newTime); + } + }; + this.tabTimeout = setTimeout(func.bind(this, [time]), time); + } + + /** + * Handler for mouse up event on the tab buttons + */ + tabMouseUp() { + this.mousedown = false; + + clearTimeout(this.tabTimeout); + this.tabTimeout = null; + } + + /** + * Handler for changing to the left tab + */ + changeTabLeft() { + const currentTab = this.manager.tabs.getActiveOutputTab(); + this.changeTab(this.getPreviousInputNum(currentTab), this.app.options.syncTabs); + } + + /** + * Handler for changing to the right tab + */ + changeTabRight() { + const currentTab = this.manager.tabs.getActiveOutputTab(); + this.changeTab(this.getNextInputNum(currentTab), this.app.options.syncTabs); + } + + /** + * Handler for go to tab button clicked + */ + goToTab() { + const min = this.getSmallestInputNum(), + max = this.getLargestInputNum(); + + let tabNum = window.prompt(`Enter tab number (${min} - ${max}):`, this.manager.tabs.getActiveOutputTab().toString()); + if (tabNum === null) return; + tabNum = parseInt(tabNum, 10); + + if (this.outputExists(tabNum)) { + this.changeTab(tabNum, this.app.options.syncTabs); + } + } + + /** + * Generates a list of the nearby inputNums + * @param inputNum + * @param direction + */ + getNearbyNums(inputNum, direction) { + const nums = []; + for (let i = 0; i < this.maxTabs; i++) { + let newNum; + if (i === 0 && this.outputs[inputNum] !== undefined) { + newNum = inputNum; + } else { + switch (direction) { + case "left": + newNum = this.getNextInputNum(nums[i - 1]); + if (newNum === nums[i - 1]) { + direction = "right"; + newNum = this.getPreviousInputNum(nums[0]); + } + break; + case "right": + newNum = this.getPreviousInputNum(nums[i - 1]); + if (newNum === nums[i - 1]) { + direction = "left"; + newNum = this.getNextInputNum(nums[0]); + } + } + } + if (!nums.includes(newNum) && (newNum > 0)) { + nums.push(newNum); + } + } + nums.sort(function(a, b) { + return a - b; + }); + return nums; + } + + /** + * Gets the largest inputNum + * + * @returns {number} + */ + getLargestInputNum() { + const inputNums = Object.keys(this.outputs); + if (inputNums.length === 0) return -1; + return Math.max(...inputNums); + } + + /** + * Gets the smallest inputNum + * + * @returns {number} + */ + getSmallestInputNum() { + const inputNums = Object.keys(this.outputs); + if (inputNums.length === 0) return -1; + return Math.min(...inputNums); + } + + /** + * Gets the previous inputNum + * + * @param {number} inputNum - The current input number + * @returns {number} + */ + getPreviousInputNum(inputNum) { + const inputNums = Object.keys(this.outputs); + if (inputNums.length === 0) return -1; + let num = Math.min(...inputNums); + for (let i = 0; i < inputNums.length; i++) { + const iNum = parseInt(inputNums[i], 10); + if (iNum < inputNum) { + if (iNum > num) { + num = iNum; + } + } + } + return num; + } + + /** + * Gets the next inputNum + * + * @param {number} inputNum - The current input number + * @returns {number} + */ + getNextInputNum(inputNum) { + const inputNums = Object.keys(this.outputs); + if (inputNums.length === 0) return -1; + let num = Math.max(...inputNums); + for (let i = 0; i < inputNums.length; i++) { + const iNum = parseInt(inputNums[i], 10); + if (iNum > inputNum) { + if (iNum < num) { + num = iNum; + } + } + } + return num; + } + + /** + * Removes a tab and it's corresponding output + * + * @param {number} inputNum + */ + removeTab(inputNum) { + if (!this.outputExists(inputNum)) return; + + const tabElement = this.manager.tabs.getOutputTabItem(inputNum); + + this.removeOutput(inputNum); + + if (tabElement !== null) { + this.refreshTabs(this.getPreviousInputNum(inputNum), "left"); + } + } + + /** + * Redraw the entire tab bar to remove any outdated tabs + * @param {number} activeTab + * @param {string} direction - Either "left" or "right" + */ + refreshTabs(activeTab, direction) { + const newNums = this.getNearbyNums(activeTab, direction), + tabsLeft = (newNums[0] !== this.getSmallestInputNum() && newNums.length > 0), + tabsRight = (newNums[newNums.length - 1] !== this.getLargestInputNum() && newNums.length > 0); + + this.manager.tabs.refreshOutputTabs(newNums, activeTab, tabsLeft, tabsRight); + + for (let i = 0; i < newNums.length; i++) { + this.displayTabInfo(newNums[i]); + } + + } + + /** + * Display output information in the tab header + * + * @param {number} inputNum + */ + async displayTabInfo(inputNum) { + if (!this.outputExists(inputNum)) return; + + const dish = this.getOutputDish(inputNum); + let tabStr = ""; + + if (dish !== null) { + tabStr = await this.getDishTitle(this.getOutputDish(inputNum), 100); + tabStr = tabStr.replace(/[\n\r]/g, ""); + } + this.manager.tabs.updateOutputTabHeader(inputNum, tabStr); + if (this.manager.worker.recipeConfig !== undefined) { + this.manager.tabs.updateOutputTabProgress(inputNum, this.outputs[inputNum].progress, this.manager.worker.recipeConfig.length); + } + + const tabItem = this.manager.tabs.getOutputTabItem(inputNum); + if (tabItem) { + if (this.outputs[inputNum].status === "error") { + tabItem.style.color = "#FF0000"; + } else { + tabItem.style.color = ""; + } + } + } + + /** + * Displays information about the output. + * + * @param {number} length - The length of the current output string + * @param {number} lines - The number of the lines in the current output string + * @param {number} duration - The length of time (ms) it took to generate the output + */ + setOutputInfo(length, lines, duration) { + if (!length) return; + let width = length.toString().length; + width = width < 4 ? 4 : width; + + const lengthStr = length.toString().padStart(width, " ").replace(/ /g, " "); + const timeStr = (duration.toString() + "ms").padStart(width, " ").replace(/ /g, " "); + + let msg = "time: " + timeStr + "
    length: " + lengthStr; + + if (typeof lines === "number") { + const linesStr = lines.toString().padStart(width, " ").replace(/ /g, " "); + msg += "
    lines: " + linesStr; + } + + document.getElementById("output-info").innerHTML = msg; + document.getElementById("input-selection-info").innerHTML = ""; + document.getElementById("output-selection-info").innerHTML = ""; + } + + /** + * Triggers the BackgroundWorker to attempt Magic on the current output. + */ + async backgroundMagic() { + this.hideMagicButton(); + const dish = this.getOutputDish(this.manager.tabs.getActiveOutputTab()); + if (!this.app.options.autoMagic || dish === null) return; + const buffer = await this.getDishBuffer(dish); + const sample = buffer.slice(0, 1000) || ""; + + if (sample.length || sample.byteLength) { + this.manager.background.magic(sample); + } + } + + /** + * Handles the results of a background Magic call. + * + * @param {Object[]} options + */ + backgroundMagicResult(options) { + if (!options.length || + !options[0].recipe.length) + return; + + const currentRecipeConfig = this.app.getRecipeConfig(); + const newRecipeConfig = currentRecipeConfig.concat(options[0].recipe); + const opSequence = options[0].recipe.map(o => o.op).join(", "); + + this.showMagicButton(opSequence, options[0].data, newRecipeConfig); + } + + /** + * Handler for Magic click events. + * + * Loads the Magic recipe. + * + * @fires Manager#statechange + */ + magicClick() { + const magicButton = document.getElementById("magic"); + this.app.setRecipeConfig(JSON.parse(magicButton.getAttribute("data-recipe"))); + window.dispatchEvent(this.manager.statechange); + this.hideMagicButton(); + } + + /** + * Displays the Magic button with a title and adds a link to a complete recipe. + * + * @param {string} opSequence + * @param {string} result + * @param {Object[]} recipeConfig + */ + showMagicButton(opSequence, result, recipeConfig) { + const magicButton = document.getElementById("magic"); + magicButton.setAttribute("data-original-title", `${opSequence} will produce "${Utils.escapeHtml(Utils.truncate(result), 30)}"`); + magicButton.setAttribute("data-recipe", JSON.stringify(recipeConfig), null, ""); + magicButton.classList.remove("hidden"); + } + + + /** + * Hides the Magic button and resets its values. + */ + hideMagicButton() { + const magicButton = document.getElementById("magic"); + magicButton.classList.add("hidden"); + magicButton.setAttribute("data-recipe", ""); + magicButton.setAttribute("data-original-title", "Magic!"); + } + + + /** + * Handler for file slice display events. + */ + async displayFileSlice() { + document.querySelector("#output-loader .loading-msg").textContent = "Loading file slice..."; + this.toggleLoader(true); + const outputText = document.getElementById("output-text"), + outputHtml = document.getElementById("output-html"), + outputFile = document.getElementById("output-file"), + outputHighlighter = document.getElementById("output-highlighter"), + inputHighlighter = document.getElementById("input-highlighter"), + showFileOverlay = document.getElementById("show-file-overlay"), + sliceFromEl = document.getElementById("output-file-slice-from"), + sliceToEl = document.getElementById("output-file-slice-to"), + sliceFrom = parseInt(sliceFromEl.value, 10), + sliceTo = parseInt(sliceToEl.value, 10), + output = this.outputs[this.manager.tabs.getActiveOutputTab()].data; + + let str; + if (output.type === "ArrayBuffer") { + str = Utils.arrayBufferToStr(output.result.slice(sliceFrom, sliceTo)); + } else { + str = Utils.arrayBufferToStr(await this.getDishBuffer(output.dish).slice(sliceFrom, sliceTo)); + } + + outputText.classList.remove("blur"); + showFileOverlay.style.display = "block"; + outputText.value = Utils.printable(str, true); + + + outputText.style.display = "block"; + outputHtml.style.display = "none"; + outputFile.style.display = "none"; + outputHighlighter.display = "block"; + inputHighlighter.display = "block"; + + this.toggleLoader(false); + } + + /** + * Handler for show file overlay events + * + * @param {Event} e + */ + showFileOverlayClick(e) { + const showFileOverlay = e.target; + + document.getElementById("output-text").classList.add("blur"); + showFileOverlay.style.display = "none"; + this.set(this.manager.tabs.getActiveOutputTab()); + } + + /** + * Handler for extract file events. + * + * @param {Event} e + */ + async extractFileClick(e) { + e.preventDefault(); + e.stopPropagation(); + + const el = e.target.nodeName === "I" ? e.target.parentNode : e.target; + const blobURL = el.getAttribute("blob-url"); + const fileName = el.getAttribute("file-name"); + + const blob = await fetch(blobURL).then(r => r.blob()); + this.manager.input.loadUIFiles([new File([blob], fileName, {type: blob.type})]); + } + + + /** + * Handler for copy click events. + * Copies the output to the clipboard + */ + async copyClick() { + const dish = this.getOutputDish(this.manager.tabs.getActiveOutputTab()); + if (dish === null) { + this.app.alert("Could not find data to copy. Has this output been baked yet?", 3000); + return; + } + + const output = await dish.get(Dish.STRING); + + // Create invisible textarea to populate with the raw dish string (not the printable version that + // contains dots instead of the actual bytes) + const textarea = document.createElement("textarea"); + textarea.style.position = "fixed"; + textarea.style.top = 0; + textarea.style.left = 0; + textarea.style.width = 0; + textarea.style.height = 0; + textarea.style.border = "none"; + + textarea.value = output; + document.body.appendChild(textarea); + + let success = false; + try { + textarea.select(); + success = output && document.execCommand("copy"); + } catch (err) { + success = false; + } + + if (success) { + this.app.alert("Copied raw output successfully.", 2000); + } else { + this.app.alert("Sorry, the output could not be copied.", 3000); + } + + // Clean up + document.body.removeChild(textarea); + } + + /** + * Returns true if the output contains carriage returns + * + * @returns {boolean} + */ + async containsCR() { + const dish = this.getOutputDish(this.manager.tabs.getActiveOutputTab()); + if (dish === null) return; + + if (dish.type === Dish.STRING) { + const data = await dish.get(Dish.STRING); + return data.indexOf("\r") >= 0; + } + } + + /** + * Handler for switch click events. + * Moves the current output into the input textarea. + */ + async switchClick() { + const active = await this.getDishBuffer(this.getOutputDish(this.manager.tabs.getActiveOutputTab())); + this.manager.input.inputWorker.postMessage({ + action: "inputSwitch", + data: { + inputNum: this.manager.tabs.getActiveInputTab(), + outputData: active + } + }, [active]); + } + + /** + * Handler for when the inputWorker has switched the inputs. + * Stores the old input + * + * @param {object} switchData + * @param {number} switchData.inputNum + * @param {string | object} switchData.data + * @param {ArrayBuffer} switchData.data.fileBuffer + * @param {number} switchData.data.size + * @param {string} switchData.data.type + * @param {string} switchData.data.name + */ + inputSwitch(switchData) { + this.switchOrigData = switchData; + document.getElementById("undo-switch").disabled = false; + } + + /** + * Handler for undo switch click events. + * Removes the output from the input and replaces the input that was removed. + */ + undoSwitchClick() { + this.manager.input.updateInputObj(this.switchOrigData.inputNum, this.switchOrigData.data); + const undoSwitch = document.getElementById("undo-switch"); + undoSwitch.disabled = true; + $(undoSwitch).tooltip("hide"); + + this.manager.input.inputWorker.postMessage({ + action: "setInput", + data: { + inputNum: this.switchOrigData.inputNum, + silent: false + } + }); + } + + /** + * Handler for maximise output click events. + * Resizes the output frame to be as large as possible, or restores it to its original size. + */ + maximiseOutputClick(e) { + 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); + + $(el).attr("data-original-title", "Restore output pane"); + el.querySelector("i").innerHTML = "fullscreen_exit"; + } else { + $(el).attr("data-original-title", "Maximise output pane"); + el.querySelector("i").innerHTML = "fullscreen"; + this.app.initialiseSplitter(false); + this.app.resetLayout(); + } + } + + /** + * Handler for find tab button clicked + */ + findTab() { + this.filterTabSearch(); + $("#output-tab-modal").modal(); + } + + /** + * Searches the outputs using the filter settings and displays the results + */ + async filterTabSearch() { + const showPending = document.getElementById("output-show-pending").checked, + showBaking = document.getElementById("output-show-baking").checked, + showBaked = document.getElementById("output-show-baked").checked, + showStale = document.getElementById("output-show-stale").checked, + showErrored = document.getElementById("output-show-errored").checked, + contentFilter = document.getElementById("output-content-filter").value, + resultsList = document.getElementById("output-search-results"), + numResults = parseInt(document.getElementById("output-num-results").value, 10), + inputNums = Object.keys(this.outputs), + results = []; + + let contentFilterExp; + try { + contentFilterExp = new RegExp(contentFilter, "i"); + } catch (error) { + this.app.handleError(error); + return; + } + + // Search through the outputs for matching output results + for (let i = 0; i < inputNums.length; i++) { + const iNum = inputNums[i], + output = this.outputs[iNum]; + + if (output.status === "pending" && showPending || + output.status === "baking" && showBaking || + output.status === "error" && showErrored || + output.status === "stale" && showStale || + output.status === "inactive" && showStale) { + const outDisplay = { + "pending": "Not baked yet", + "baking": "Baking", + "error": output.error || "Errored", + "stale": "Stale (output is out of date)", + "inactive": "Not baked yet" + }; + + // If the output has a dish object, check it against the filter + if (Object.prototype.hasOwnProperty.call(output, "data") && + output.data && + Object.prototype.hasOwnProperty.call(output.data, "dish")) { + const data = await output.data.dish.get(Dish.STRING); + if (contentFilterExp.test(data)) { + results.push({ + inputNum: iNum, + textDisplay: data.slice(0, 100) + }); + } + } else { + results.push({ + inputNum: iNum, + textDisplay: outDisplay[output.status] + }); + } + } else if (output.status === "baked" && showBaked && output.progress === false) { + let data = await output.data.dish.get(Dish.STRING); + data = data.replace(/[\r\n]/g, ""); + if (contentFilterExp.test(data)) { + results.push({ + inputNum: iNum, + textDisplay: data.slice(0, 100) + }); + } + } else if (output.progress !== false && showErrored) { + let data = await output.data.dish.get(Dish.STRING); + data = data.replace(/[\r\n]/g, ""); + if (contentFilterExp.test(data)) { + results.push({ + inputNum: iNum, + textDisplay: data.slice(0, 100) + }); + } + } + + if (results.length >= numResults) { + break; + } + } + + for (let i = resultsList.children.length - 1; i >= 0; i--) { + resultsList.children.item(i).remove(); + } + + for (let i = 0; i < results.length; i++) { + const newListItem = document.createElement("li"); + newListItem.classList.add("output-filter-result"); + newListItem.setAttribute("inputNum", results[i].inputNum); + newListItem.innerText = `${results[i].inputNum}: ${results[i].textDisplay}`; + + resultsList.appendChild(newListItem); + } + } + + /** + * Handler for clicking on a filter result. + * Changes to the clicked output + * + * @param {event} e + */ + filterItemClick(e) { + if (!e.target) return; + const inputNum = parseInt(e.target.getAttribute("inputNum"), 10); + if (inputNum <= 0) return; + + $("#output-tab-modal").modal("hide"); + this.changeTab(inputNum, this.app.options.syncTabs); + } +} + +export default OutputWaiter; diff --git a/src/web/RecipeWaiter.mjs b/src/web/waiters/RecipeWaiter.mjs similarity index 93% rename from src/web/RecipeWaiter.mjs rename to src/web/waiters/RecipeWaiter.mjs index f2edd725..d198098b 100755 --- a/src/web/RecipeWaiter.mjs +++ b/src/web/waiters/RecipeWaiter.mjs @@ -4,9 +4,9 @@ * @license Apache-2.0 */ -import HTMLOperation from "./HTMLOperation"; +import HTMLOperation from "../HTMLOperation.mjs"; import Sortable from "sortablejs"; -import Utils from "../core/Utils"; +import Utils from "../../core/Utils.mjs"; /** @@ -124,16 +124,21 @@ class RecipeWaiter { * @param {event} evt */ opSortEnd(evt) { - if (this.removeIntent) { - if (evt.item.parentNode.id === "rec-list") { - evt.item.remove(); - } + if (this.removeIntent && evt.item.parentNode.id === "rec-list") { + evt.item.remove(); return; } // Reinitialise the popover on the original element in the ops list because for some reason it - // gets destroyed and recreated. - this.manager.ops.enableOpsListPopovers(evt.clone); + // gets destroyed and recreated. If the clone isn't in the ops list, we use the original item instead. + let enableOpsElement; + if (evt.clone.parentNode && evt.clone.parentNode.classList.contains("op-list")) { + enableOpsElement = evt.clone; + } else { + enableOpsElement = evt.item; + $(evt.item).attr("data-toggle", "popover"); + } + this.manager.ops.enableOpsListPopovers(enableOpsElement); if (evt.item.parentNode.id !== "rec-list") { return; @@ -612,6 +617,23 @@ class RecipeWaiter { ingredientRule.style.gridTemplateColumns = "auto auto auto auto"; ingredientChildRule.style.gridColumn = "1 / span 4"; } + + // Hide Chef icon on Bake button if the page is compressed + const bakeIcon = document.querySelector("#bake img"); + + if (recList.clientWidth < 370) { + // Hide Chef icon on Bake button + bakeIcon.style.display = "none"; + } else { + bakeIcon.style.display = "inline-block"; + } + + // Scale controls to fit pane width + const controls = document.getElementById("controls"); + const controlsContent = document.getElementById("controls-content"); + const scale = (controls.clientWidth - 1) / controlsContent.scrollWidth; + + controlsContent.style.transform = `translate(-50%, -50%) scale(${scale})`; } } diff --git a/src/web/SeasonalWaiter.mjs b/src/web/waiters/SeasonalWaiter.mjs similarity index 98% rename from src/web/SeasonalWaiter.mjs rename to src/web/waiters/SeasonalWaiter.mjs index f894e951..5de29bfa 100755 --- a/src/web/SeasonalWaiter.mjs +++ b/src/web/waiters/SeasonalWaiter.mjs @@ -5,8 +5,8 @@ */ import clippy from "clippyjs"; -import "./static/clippy_assets/agents/Clippy/agent.js"; -import clippyMap from "./static/clippy_assets/agents/Clippy/map.png"; +import "../static/clippy_assets/agents/Clippy/agent.js"; +import clippyMap from "../static/clippy_assets/agents/Clippy/map.png"; /** * Waiter to handle seasonal events and easter eggs. @@ -81,7 +81,7 @@ class SeasonalWaiter {
    `; optionsBody.appendChild(optionItem); - if (!this.app.options.hasOwnProperty("clippy")) { + if (!("clippy" in this.app.options)) { this.app.options.clippy = true; } diff --git a/src/web/waiters/TabWaiter.mjs b/src/web/waiters/TabWaiter.mjs new file mode 100644 index 00000000..384b1ab7 --- /dev/null +++ b/src/web/waiters/TabWaiter.mjs @@ -0,0 +1,428 @@ +/** + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +/** + * Waiter to handle events related to the input and output tabs + */ +class TabWaiter { + + /** + * TabWaiter constructor. + * + * @param {App} app - The main view object for CyberChef. + * @param {Manager} manager - The CyberChef event manager + */ + constructor(app, manager) { + this.app = app; + this.manager = manager; + } + + /** + * Calculates the maximum number of tabs to display + * + * @returns {number} + */ + calcMaxTabs() { + let numTabs = Math.floor((document.getElementById("IO").offsetWidth - 75) / 120); + numTabs = (numTabs > 1) ? numTabs : 2; + + return numTabs; + } + + /** + * Gets the currently active input or active tab number + * + * @param {string} io - Either "input" or "output" + * @returns {number} - The currently active tab or -1 + */ + getActiveTab(io) { + const activeTabs = document.getElementsByClassName(`active-${io}-tab`); + if (activeTabs.length > 0) { + if (!activeTabs.item(0).hasAttribute("inputNum")) return -1; + const tabNum = activeTabs.item(0).getAttribute("inputNum"); + return parseInt(tabNum, 10); + } + return -1; + } + + /** + * Gets the currently active input tab number + * + * @returns {number} + */ + getActiveInputTab() { + return this.getActiveTab("input"); + } + + /** + * Gets the currently active output tab number + * + * @returns {number} + */ + getActiveOutputTab() { + return this.getActiveTab("output"); + } + + /** + * Gets the li element for the tab of a given input number + * + * @param {number} inputNum - The inputNum of the tab we're trying to get + * @param {string} io - Either "input" or "output" + * @returns {Element} + */ + getTabItem(inputNum, io) { + const tabs = document.getElementById(`${io}-tabs`).children; + for (let i = 0; i < tabs.length; i++) { + if (parseInt(tabs.item(i).getAttribute("inputNum"), 10) === inputNum) { + return tabs.item(i); + } + } + return null; + } + + /** + * Gets the li element for an input tab of the given input number + * + * @param {inputNum} - The inputNum of the tab we're trying to get + * @returns {Element} + */ + getInputTabItem(inputNum) { + return this.getTabItem(inputNum, "input"); + } + + /** + * Gets the li element for an output tab of the given input number + * + * @param {number} inputNum + * @returns {Element} + */ + getOutputTabItem(inputNum) { + return this.getTabItem(inputNum, "output"); + } + + /** + * Gets a list of tab numbers for the currently displayed tabs + * + * @param {string} io - Either "input" or "output" + * @returns {number[]} + */ + getTabList(io) { + const nums = [], + tabs = document.getElementById(`${io}-tabs`).children; + + for (let i = 0; i < tabs.length; i++) { + nums.push(parseInt(tabs.item(i).getAttribute("inputNum"), 10)); + } + + return nums; + } + + /** + * Gets a list of tab numbers for the currently displayed input tabs + * + * @returns {number[]} + */ + getInputTabList() { + return this.getTabList("input"); + } + + /** + * Gets a list of tab numbers for the currently displayed output tabs + * + * @returns {number[]} + */ + getOutputTabList() { + return this.getTabList("output"); + } + + /** + * Creates a new tab element for the tab bar + * + * @param {number} inputNum - The inputNum of the new tab + * @param {boolean} active - If true, sets the tab to active + * @param {string} io - Either "input" or "output" + * @returns {Element} + */ + createTabElement(inputNum, active, io) { + const newTab = document.createElement("li"); + newTab.setAttribute("inputNum", inputNum.toString()); + + if (active) newTab.classList.add(`active-${io}-tab`); + + const newTabContent = document.createElement("div"); + newTabContent.classList.add(`${io}-tab-content`); + + newTabContent.innerText = `Tab ${inputNum.toString()}`; + + newTabContent.addEventListener("wheel", this.manager[io].scrollTab.bind(this.manager[io]), {passive: false}); + + newTab.appendChild(newTabContent); + + if (io === "input") { + const newTabButton = document.createElement("button"), + newTabButtonIcon = document.createElement("i"); + newTabButton.type = "button"; + newTabButton.className = "btn btn-primary bmd-btn-icon btn-close-tab"; + + newTabButtonIcon.classList.add("material-icons"); + newTabButtonIcon.innerText = "clear"; + + newTabButton.appendChild(newTabButtonIcon); + + newTabButton.addEventListener("click", this.manager.input.removeTabClick.bind(this.manager.input)); + + newTab.appendChild(newTabButton); + } + + return newTab; + } + + /** + * Creates a new tab element for the input tab bar + * + * @param {number} inputNum - The inputNum of the new input tab + * @param {boolean} [active=false] - If true, sets the tab to active + * @returns {Element} + */ + createInputTabElement(inputNum, active=false) { + return this.createTabElement(inputNum, active, "input"); + } + + /** + * Creates a new tab element for the output tab bar + * + * @param {number} inputNum - The inputNum of the new output tab + * @param {boolean} [active=false] - If true, sets the tab to active + * @returns {Element} + */ + createOutputTabElement(inputNum, active=false) { + return this.createTabElement(inputNum, active, "output"); + } + + /** + * Displays the tab bar for both the input and output + */ + showTabBar() { + document.getElementById("input-tabs-wrapper").style.display = "block"; + document.getElementById("output-tabs-wrapper").style.display = "block"; + + document.getElementById("input-wrapper").classList.add("show-tabs"); + document.getElementById("output-wrapper").classList.add("show-tabs"); + + document.getElementById("save-all-to-file").style.display = "inline-block"; + } + + /** + * Hides the tab bar for both the input and output + */ + hideTabBar() { + document.getElementById("input-tabs-wrapper").style.display = "none"; + document.getElementById("output-tabs-wrapper").style.display = "none"; + + document.getElementById("input-wrapper").classList.remove("show-tabs"); + document.getElementById("output-wrapper").classList.remove("show-tabs"); + + document.getElementById("save-all-to-file").style.display = "none"; + } + + /** + * Redraws the tab bar with an updated list of tabs, then changes to activeTab + * + * @param {number[]} nums - The inputNums of the tab bar to be drawn + * @param {number} activeTab - The inputNum of the activeTab + * @param {boolean} tabsLeft - True if there are tabs to the left of the displayed tabs + * @param {boolean} tabsRight - True if there are tabs to the right of the displayed tabs + * @param {string} io - Either "input" or "output" + */ + refreshTabs(nums, activeTab, tabsLeft, tabsRight, io) { + const tabsList = document.getElementById(`${io}-tabs`); + + // Remove existing tab elements + for (let i = tabsList.children.length - 1; i >= 0; i--) { + tabsList.children.item(i).remove(); + } + + // Create and add new tab elements + for (let i = 0; i < nums.length; i++) { + const active = (nums[i] === activeTab); + tabsList.appendChild(this.createTabElement(nums[i], active, io)); + } + + // Display shadows if there are tabs left / right of the displayed tabs + if (tabsLeft) { + tabsList.classList.add("tabs-left"); + } else { + tabsList.classList.remove("tabs-left"); + } + if (tabsRight) { + tabsList.classList.add("tabs-right"); + } else { + tabsList.classList.remove("tabs-right"); + } + + // Show or hide the tab bar depending on how many tabs we have + if (nums.length > 1) { + this.showTabBar(); + } else { + this.hideTabBar(); + } + } + + /** + * Refreshes the input tabs, and changes to activeTab + * + * @param {number[]} nums - The inputNums to be displayed as tabs + * @param {number} activeTab - The tab to change to + * @param {boolean} tabsLeft - True if there are input tabs to the left of the displayed tabs + * @param {boolean} tabsRight - True if there are input tabs to the right of the displayed tabs + */ + refreshInputTabs(nums, activeTab, tabsLeft, tabsRight) { + this.refreshTabs(nums, activeTab, tabsLeft, tabsRight, "input"); + } + + /** + * Refreshes the output tabs, and changes to activeTab + * + * @param {number[]} nums - The inputNums to be displayed as tabs + * @param {number} activeTab - The tab to change to + * @param {boolean} tabsLeft - True if there are output tabs to the left of the displayed tabs + * @param {boolean} tabsRight - True if there are output tabs to the right of the displayed tabs + */ + refreshOutputTabs(nums, activeTab, tabsLeft, tabsRight) { + this.refreshTabs(nums, activeTab, tabsLeft, tabsRight, "output"); + } + + /** + * Changes the active tab to a different tab + * + * @param {number} inputNum - The inputNum of the tab to change to + * @param {string} io - Either "input" or "output" + * @return {boolean} - False if the tab is not currently being displayed + */ + changeTab(inputNum, io) { + const tabsList = document.getElementById(`${io}-tabs`); + + this.manager.highlighter.removeHighlights(); + getSelection().removeAllRanges(); + + let found = false; + for (let i = 0; i < tabsList.children.length; i++) { + const tabNum = parseInt(tabsList.children.item(i).getAttribute("inputNum"), 10); + if (tabNum === inputNum) { + tabsList.children.item(i).classList.add(`active-${io}-tab`); + found = true; + } else { + tabsList.children.item(i).classList.remove(`active-${io}-tab`); + } + } + + return found; + } + + /** + * Changes the active input tab to a different tab + * + * @param {number} inputNum + * @returns {boolean} - False if the tab is not currently being displayed + */ + changeInputTab(inputNum) { + return this.changeTab(inputNum, "input"); + } + + /** + * Changes the active output tab to a different tab + * + * @param {number} inputNum + * @returns {boolean} - False if the tab is not currently being displayed + */ + changeOutputTab(inputNum) { + return this.changeTab(inputNum, "output"); + } + + /** + * Updates the tab header to display a preview of the tab contents + * + * @param {number} inputNum - The inputNum of the tab to update the header of + * @param {string} data - The data to display in the tab header + * @param {string} io - Either "input" or "output" + */ + updateTabHeader(inputNum, data, io) { + const tab = this.getTabItem(inputNum, io); + if (tab === null) return; + + let headerData = `Tab ${inputNum}`; + if (data.length > 0) { + headerData = data.slice(0, 100); + headerData = `${inputNum}: ${headerData}`; + } + tab.firstElementChild.innerText = headerData; + } + + /** + * Updates the input tab header to display a preview of the tab contents + * + * @param {number} inputNum - The inputNum of the tab to update the header of + * @param {string} data - The data to display in the tab header + */ + updateInputTabHeader(inputNum, data) { + this.updateTabHeader(inputNum, data, "input"); + } + + /** + * Updates the output tab header to display a preview of the tab contents + * + * @param {number} inputNum - The inputNum of the tab to update the header of + * @param {string} data - The data to display in the tab header + */ + updateOutputTabHeader(inputNum, data) { + this.updateTabHeader(inputNum, data, "output"); + } + + /** + * Updates the tab background to display the progress of the current tab + * + * @param {number} inputNum - The inputNum of the tab + * @param {number} progress - The current progress + * @param {number} total - The total which the progress is a percent of + * @param {string} io - Either "input" or "output" + */ + updateTabProgress(inputNum, progress, total, io) { + const tabItem = this.getTabItem(inputNum, io); + if (tabItem === null) return; + + const percentComplete = (progress / total) * 100; + if (percentComplete >= 100 || progress === false) { + tabItem.style.background = ""; + } else { + tabItem.style.background = `linear-gradient(to right, var(--title-background-colour) ${percentComplete}%, var(--primary-background-colour) ${percentComplete}%)`; + } + } + + /** + * Updates the input tab background to display its progress + * + * @param {number} inputNum + * @param {number} progress + * @param {number} total + */ + updateInputTabProgress(inputNum, progress, total) { + this.updateTabProgress(inputNum, progress, total, "input"); + } + + /** + * Updates the output tab background to display its progress + * + * @param {number} inputNum + * @param {number} progress + * @param {number} total + */ + updateOutputTabProgress(inputNum, progress, total) { + this.updateTabProgress(inputNum, progress, total, "output"); + } + +} + +export default TabWaiter; diff --git a/src/web/WindowWaiter.mjs b/src/web/waiters/WindowWaiter.mjs similarity index 92% rename from src/web/WindowWaiter.mjs rename to src/web/waiters/WindowWaiter.mjs index a8e124f5..5b44ff98 100755 --- a/src/web/WindowWaiter.mjs +++ b/src/web/waiters/WindowWaiter.mjs @@ -25,8 +25,7 @@ class WindowWaiter { * continuous resetting). */ windowResize() { - clearTimeout(this.resetLayoutTimeout); - this.resetLayoutTimeout = setTimeout(this.app.resetLayout.bind(this.app), 200); + this.app.debounce(this.app.resetLayout, 200, "windowResize", this.app, [])(); } diff --git a/src/web/waiters/WorkerWaiter.mjs b/src/web/waiters/WorkerWaiter.mjs new file mode 100644 index 00000000..f4a5475f --- /dev/null +++ b/src/web/waiters/WorkerWaiter.mjs @@ -0,0 +1,817 @@ +/** + * @author n1474335 [n1474335@gmail.com] + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import ChefWorker from "worker-loader?inline&fallback=false!../../core/ChefWorker"; +import DishWorker from "worker-loader?inline&fallback=false!../workers/DishWorker"; + +/** + * Waiter to handle conversations with the ChefWorker + */ +class WorkerWaiter { + + /** + * WorkerWaiter constructor + * + * @param {App} app - The main view object for CyberChef + * @param {Manager} manager - The CyberChef event manager + */ + constructor(app, manager) { + this.app = app; + this.manager = manager; + + this.loaded = false; + this.chefWorkers = []; + this.inputs = []; + this.inputNums = []; + this.totalOutputs = 0; + this.loadingOutputs = 0; + this.bakeId = 0; + this.callbacks = {}; + this.callbackID = 0; + + this.maxWorkers = 1; + if (navigator.hardwareConcurrency !== undefined && + navigator.hardwareConcurrency > 1) { + this.maxWorkers = navigator.hardwareConcurrency - 1; + } + + // Store dishWorker action (getDishAs or getDishTitle) + this.dishWorker = { + worker: null, + currentAction: "" + }; + this.dishWorkerQueue = []; + } + + /** + * Terminates any existing ChefWorkers and sets up a new worker + */ + setupChefWorker() { + for (let i = this.chefWorkers.length - 1; i >= 0; i--) { + this.removeChefWorker(this.chefWorkers[i]); + } + + this.addChefWorker(); + this.setupDishWorker(); + } + + /** + * Sets up a DishWorker to be used for performing Dish operations + */ + setupDishWorker() { + if (this.dishWorker.worker !== null) { + this.dishWorker.worker.terminate(); + this.dishWorker.currentAction = ""; + } + log.debug("Adding new DishWorker"); + + this.dishWorker.worker = new DishWorker(); + this.dishWorker.worker.addEventListener("message", this.handleDishMessage.bind(this)); + + if (this.dishWorkerQueue.length > 0) { + this.postDishMessage(this.dishWorkerQueue.splice(0, 1)[0]); + } + } + + /** + * Adds a new ChefWorker + * + * @returns {number} The index of the created worker + */ + addChefWorker() { + if (this.chefWorkers.length === this.maxWorkers) { + // Can't create any more workers + return -1; + } + + log.debug("Adding new ChefWorker"); + + // Create a new ChefWorker and send it the docURL + const newWorker = new ChefWorker(); + newWorker.addEventListener("message", this.handleChefMessage.bind(this)); + let docURL = document.location.href.split(/[#?]/)[0]; + const index = docURL.lastIndexOf("/"); + if (index > 0) { + docURL = docURL.substring(0, index); + } + + newWorker.postMessage({"action": "docURL", "data": docURL}); + newWorker.postMessage({ + action: "setLogLevel", + data: log.getLevel() + }); + + // Store the worker, whether or not it's active, and the inputNum as an object + const newWorkerObj = { + worker: newWorker, + active: false, + inputNum: -1 + }; + + this.chefWorkers.push(newWorkerObj); + return this.chefWorkers.indexOf(newWorkerObj); + } + + /** + * Gets an inactive ChefWorker to be used for baking + * + * @param {boolean} [setActive=true] - If true, set the worker status to active + * @returns {number} - The index of the ChefWorker + */ + getInactiveChefWorker(setActive=true) { + for (let i = 0; i < this.chefWorkers.length; i++) { + if (!this.chefWorkers[i].active) { + this.chefWorkers[i].active = setActive; + return i; + } + } + return -1; + } + + /** + * Removes a ChefWorker + * + * @param {Object} workerObj + */ + removeChefWorker(workerObj) { + const index = this.chefWorkers.indexOf(workerObj); + if (index === -1) { + return; + } + + if (this.chefWorkers.length > 1 || this.chefWorkers[index].active) { + log.debug(`Removing ChefWorker at index ${index}`); + this.chefWorkers[index].worker.terminate(); + this.chefWorkers.splice(index, 1); + } + + // There should always be a ChefWorker loaded + if (this.chefWorkers.length === 0) { + this.addChefWorker(); + } + } + + /** + * Finds and returns the object for the ChefWorker of a given inputNum + * + * @param {number} inputNum + */ + getChefWorker(inputNum) { + for (let i = 0; i < this.chefWorkers.length; i++) { + if (this.chefWorkers[i].inputNum === inputNum) { + return this.chefWorkers[i]; + } + } + } + + /** + * Handler for messages sent back by the ChefWorkers + * + * @param {MessageEvent} e + */ + handleChefMessage(e) { + const r = e.data; + let inputNum = 0; + log.debug(`Receiving ${r.action} from ChefWorker.`); + + if (Object.prototype.hasOwnProperty.call(r.data, "inputNum")) { + inputNum = r.data.inputNum; + } + + const currentWorker = this.getChefWorker(inputNum); + + switch (r.action) { + case "bakeComplete": + log.debug(`Bake ${inputNum} complete.`); + + if (r.data.error) { + this.app.handleError(r.data.error); + this.manager.output.updateOutputError(r.data.error, inputNum, r.data.progress); + } else { + this.updateOutput(r.data, r.data.inputNum, r.data.bakeId, r.data.progress); + } + + this.app.progress = r.data.progress; + + if (r.data.progress === this.recipeConfig.length) { + this.step = false; + } + + this.workerFinished(currentWorker); + break; + case "bakeError": + this.app.handleError(r.data.error); + this.manager.output.updateOutputError(r.data.error, inputNum, r.data.progress); + this.app.progress = r.data.progress; + this.workerFinished(currentWorker); + break; + case "dishReturned": + this.callbacks[r.data.id](r.data); + break; + case "silentBakeComplete": + break; + case "workerLoaded": + this.app.workerLoaded = true; + log.debug("ChefWorker loaded."); + if (!this.loaded) { + this.app.loaded(); + this.loaded = true; + } else { + this.bakeNextInput(this.getInactiveChefWorker(false)); + } + break; + case "statusMessage": + this.manager.output.updateOutputMessage(r.data.message, r.data.inputNum, true); + break; + case "progressMessage": + this.manager.output.updateOutputProgress(r.data.progress, r.data.total, r.data.inputNum); + break; + case "optionUpdate": + log.debug(`Setting ${r.data.option} to ${r.data.value}`); + this.app.options[r.data.option] = r.data.value; + break; + case "setRegisters": + this.manager.recipe.setRegisters(r.data.opIndex, r.data.numPrevRegisters, r.data.registers); + break; + case "highlightsCalculated": + this.manager.highlighter.displayHighlights(r.data.pos, r.data.direction); + break; + default: + log.error("Unrecognised message from ChefWorker", e); + break; + } + } + + /** + * Update the value of an output + * + * @param {Object} data + * @param {number} inputNum + * @param {number} bakeId + * @param {number} progress + */ + updateOutput(data, inputNum, bakeId, progress) { + this.manager.output.updateOutputBakeId(bakeId, inputNum); + if (progress === this.recipeConfig.length) { + progress = false; + } + this.manager.output.updateOutputProgress(progress, this.recipeConfig.length, inputNum); + this.manager.output.updateOutputValue(data, inputNum, false); + + if (progress !== false) { + this.manager.output.updateOutputStatus("error", inputNum); + + if (inputNum === this.manager.tabs.getActiveInputTab()) { + this.manager.recipe.updateBreakpointIndicator(progress); + } + + } else { + this.manager.output.updateOutputStatus("baked", inputNum); + } + } + + /** + * Updates the UI to show if baking is in progress or not. + * + * @param {boolean} bakingStatus + */ + setBakingStatus(bakingStatus) { + this.app.baking = bakingStatus; + this.app.debounce(this.manager.controls.toggleBakeButtonFunction, 20, "toggleBakeButton", this, [bakingStatus ? "cancel" : "bake"])(); + + if (bakingStatus) this.manager.output.hideMagicButton(); + } + + /** + * Get the progress of the ChefWorkers + */ + getBakeProgress() { + const pendingInputs = this.inputNums.length + this.loadingOutputs + this.inputs.length; + let bakingInputs = 0; + + for (let i = 0; i < this.chefWorkers.length; i++) { + if (this.chefWorkers[i].active) { + bakingInputs++; + } + } + + const total = this.totalOutputs; + const bakedInputs = total - pendingInputs - bakingInputs; + + return { + total: total, + pending: pendingInputs, + baking: bakingInputs, + baked: bakedInputs + }; + } + + /** + * Cancels the current bake by terminating and removing all ChefWorkers + * + * @param {boolean} [silent=false] - If true, don't set the output + * @param {boolean} killAll - If true, kills all chefWorkers regardless of status + */ + cancelBake(silent, killAll) { + for (let i = this.chefWorkers.length - 1; i >= 0; i--) { + if (this.chefWorkers[i].active || killAll) { + const inputNum = this.chefWorkers[i].inputNum; + this.removeChefWorker(this.chefWorkers[i]); + this.manager.output.updateOutputStatus("inactive", inputNum); + } + } + this.setBakingStatus(false); + + for (let i = 0; i < this.inputs.length; i++) { + this.manager.output.updateOutputStatus("inactive", this.inputs[i].inputNum); + } + + for (let i = 0; i < this.inputNums.length; i++) { + this.manager.output.updateOutputStatus("inactive", this.inputNums[i]); + } + + const tabList = this.manager.tabs.getOutputTabList(); + for (let i = 0; i < tabList.length; i++) { + this.manager.tabs.getOutputTabItem(tabList[i]).style.background = ""; + } + + this.inputs = []; + this.inputNums = []; + this.totalOutputs = 0; + this.loadingOutputs = 0; + if (!silent) this.manager.output.set(this.manager.tabs.getActiveOutputTab()); + } + + /** + * Handle a worker completing baking + * + * @param {object} workerObj - Object containing the worker information + * @param {ChefWorker} workerObj.worker - The actual worker object + * @param {number} workerObj.inputNum - The inputNum of the input being baked by the worker + * @param {boolean} workerObj.active - If true, the worker is currrently baking an input + */ + workerFinished(workerObj) { + const workerIdx = this.chefWorkers.indexOf(workerObj); + this.chefWorkers[workerIdx].active = false; + if (this.inputs.length > 0) { + this.bakeNextInput(workerIdx); + } else if (this.inputNums.length === 0 && this.loadingOutputs === 0) { + // The ChefWorker is no longer needed + log.debug("No more inputs to bake."); + const progress = this.getBakeProgress(); + if (progress.total === progress.baked) { + this.bakingComplete(); + } + } + } + + /** + * Handler for completed bakes + */ + bakingComplete() { + this.setBakingStatus(false); + let duration = new Date().getTime() - this.bakeStartTime; + duration = duration.toLocaleString() + "ms"; + const progress = this.getBakeProgress(); + + if (progress.total > 1) { + let width = progress.total.toLocaleString().length; + if (duration.length > width) { + width = duration.length; + } + width = width < 2 ? 2 : width; + + const totalStr = progress.total.toLocaleString().padStart(width, " ").replace(/ /g, " "); + const durationStr = duration.padStart(width, " ").replace(/ /g, " "); + + const inputNums = Object.keys(this.manager.output.outputs); + let avgTime = 0, + numOutputs = 0; + for (let i = 0; i < inputNums.length; i++) { + const output = this.manager.output.outputs[inputNums[i]]; + if (output.status === "baked") { + numOutputs++; + avgTime += output.data.duration; + } + } + avgTime = Math.round(avgTime / numOutputs).toLocaleString() + "ms"; + avgTime = avgTime.padStart(width, " ").replace(/ /g, " "); + + const msg = `total: ${totalStr}
    time: ${durationStr}
    average: ${avgTime}`; + + const bakeInfo = document.getElementById("bake-info"); + bakeInfo.innerHTML = msg; + bakeInfo.style.display = ""; + } else { + document.getElementById("bake-info").style.display = "none"; + } + + document.getElementById("bake").style.background = ""; + this.totalOutputs = 0; // Reset for next time + log.debug("--- Bake complete ---"); + } + + /** + * Bakes the next input and tells the inputWorker to load the next input + * + * @param {number} workerIdx - The index of the worker to bake with + */ + bakeNextInput(workerIdx) { + if (this.inputs.length === 0) return; + if (workerIdx === -1) return; + if (!this.chefWorkers[workerIdx]) return; + this.chefWorkers[workerIdx].active = true; + const nextInput = this.inputs.splice(0, 1)[0]; + if (typeof nextInput.inputNum === "string") nextInput.inputNum = parseInt(nextInput.inputNum, 10); + + log.debug(`Baking input ${nextInput.inputNum}.`); + this.manager.output.updateOutputMessage(`Baking input ${nextInput.inputNum}...`, nextInput.inputNum, false); + this.manager.output.updateOutputStatus("baking", nextInput.inputNum); + + this.chefWorkers[workerIdx].inputNum = nextInput.inputNum; + const input = nextInput.input, + recipeConfig = this.recipeConfig; + + if (this.step) { + // Remove all breakpoints from the recipe up to progress + if (nextInput.progress !== false) { + for (let i = 0; i < nextInput.progress; i++) { + if ("breakpoint" in recipeConfig[i]) { + delete recipeConfig[i].breakpoint; + } + } + } + + // Set a breakpoint at the next operation so we stop baking there + if (recipeConfig[this.app.progress]) recipeConfig[this.app.progress].breakpoint = true; + } + + let transferable; + if (input instanceof ArrayBuffer || ArrayBuffer.isView(input)) { + transferable = [input]; + } + this.chefWorkers[workerIdx].worker.postMessage({ + action: "bake", + data: { + input: input, + recipeConfig: recipeConfig, + options: this.options, + inputNum: nextInput.inputNum, + bakeId: this.bakeId + } + }, transferable); + + if (this.inputNums.length > 0) { + this.manager.input.inputWorker.postMessage({ + action: "bakeNext", + data: { + inputNum: this.inputNums.splice(0, 1)[0], + bakeId: this.bakeId + } + }); + this.loadingOutputs++; + } + } + + /** + * Bakes the current input using the current recipe. + * + * @param {Object[]} recipeConfig + * @param {Object} options + * @param {number} progress + * @param {boolean} step + */ + bake(recipeConfig, options, progress, step) { + this.setBakingStatus(true); + this.manager.recipe.updateBreakpointIndicator(false); + this.bakeStartTime = new Date().getTime(); + this.bakeId++; + this.recipeConfig = recipeConfig; + this.options = options; + this.progress = progress; + this.step = step; + + this.displayProgress(); + } + + /** + * Queues an input ready to be baked + * + * @param {object} inputData + * @param {string | ArrayBuffer} inputData.input + * @param {number} inputData.inputNum + * @param {number} inputData.bakeId + */ + queueInput(inputData) { + this.loadingOutputs--; + if (this.app.baking && inputData.bakeId === this.bakeId) { + this.inputs.push(inputData); + this.bakeNextInput(this.getInactiveChefWorker(true)); + } + } + + /** + * Handles if an error is thrown by QueueInput + * + * @param {object} inputData + * @param {number} inputData.inputNum + * @param {number} inputData.bakeId + */ + queueInputError(inputData) { + this.loadingOutputs--; + if (this.app.baking && inputData.bakeId === this.bakeId) { + this.manager.output.updateOutputError("Error queueing the input for a bake.", inputData.inputNum, 0); + + if (this.inputNums.length === 0) return; + + // Load the next input + this.manager.input.inputWorker.postMessage({ + action: "bakeNext", + data: { + inputNum: this.inputNums.splice(0, 1)[0], + bakeId: this.bakeId + } + }); + this.loadingOutputs++; + + } + } + + /** + * Queues a list of inputNums to be baked by ChefWorkers, and begins baking + * + * @param {object} inputData + * @param {number[]} inputData.nums - The inputNums to be queued for baking + * @param {boolean} inputData.step - If true, only execute the next operation in the recipe + * @param {number} inputData.progress - The current progress through the recipe. Used when stepping + */ + async bakeAllInputs(inputData) { + return await new Promise(resolve => { + if (this.app.baking) return; + const inputNums = inputData.nums; + const step = inputData.step; + + // Use cancelBake to clear out the inputs + this.cancelBake(true, false); + + this.inputNums = inputNums; + this.totalOutputs = inputNums.length; + this.app.progress = inputData.progress; + + let inactiveWorkers = 0; + for (let i = 0; i < this.chefWorkers.length; i++) { + if (!this.chefWorkers[i].active) { + inactiveWorkers++; + } + } + + for (let i = 0; i < inputNums.length - inactiveWorkers; i++) { + if (this.addChefWorker() === -1) break; + } + + this.app.bake(step); + + for (let i = 0; i < this.inputNums.length; i++) { + this.manager.output.updateOutputMessage(`Input ${inputNums[i]} has not been baked yet.`, inputNums[i], false); + this.manager.output.updateOutputStatus("pending", inputNums[i]); + } + + let numBakes = this.chefWorkers.length; + if (this.inputNums.length < numBakes) { + numBakes = this.inputNums.length; + } + for (let i = 0; i < numBakes; i++) { + this.manager.input.inputWorker.postMessage({ + action: "bakeNext", + data: { + inputNum: this.inputNums.splice(0, 1)[0], + bakeId: this.bakeId + } + }); + this.loadingOutputs++; + } + }); + } + + /** + * Asks the ChefWorker to run a silent bake, forcing the browser to load and cache all the relevant + * JavaScript code needed to do a real bake. + * + * @param {Object[]} [recipeConfig] + */ + silentBake(recipeConfig) { + // If there aren't any active ChefWorkers, try to add one + let workerId = this.getInactiveChefWorker(); + if (workerId === -1) { + workerId = this.addChefWorker(); + } + if (workerId === -1) return; + this.chefWorkers[workerId].worker.postMessage({ + action: "silentBake", + data: { + recipeConfig: recipeConfig + } + }); + } + + /** + * Handler for messages sent back from DishWorker + * + * @param {MessageEvent} e + */ + handleDishMessage(e) { + const r = e.data; + log.debug(`Receiving ${r.action} from DishWorker`); + + switch (r.action) { + case "dishReturned": + this.dishWorker.currentAction = ""; + this.callbacks[r.data.id](r.data); + + if (this.dishWorkerQueue.length > 0) { + this.postDishMessage(this.dishWorkerQueue.splice(0, 1)[0]); + } + + break; + default: + log.error("Unrecognised message from DishWorker", e); + break; + } + } + + /** + * Asks the ChefWorker to return the dish as the specified type + * + * @param {Dish} dish + * @param {string} type + * @param {Function} callback + */ + getDishAs(dish, type, callback) { + const id = this.callbackID++; + + this.callbacks[id] = callback; + + if (this.dishWorker.worker === null) this.setupDishWorker(); + this.postDishMessage({ + action: "getDishAs", + data: { + dish: dish, + type: type, + id: id + } + }); + } + + /** + * Asks the ChefWorker to get the title of the dish + * + * @param {Dish} dish + * @param {number} maxLength + * @param {Function} callback + * @returns {string} + */ + getDishTitle(dish, maxLength, callback) { + const id = this.callbackID++; + + this.callbacks[id] = callback; + + if (this.dishWorker.worker === null) this.setupDishWorker(); + + this.postDishMessage({ + action: "getDishTitle", + data: { + dish: dish, + maxLength: maxLength, + id: id + } + }); + } + + /** + * Queues a message to be sent to the dishWorker + * + * @param {object} message + * @param {string} message.action + * @param {object} message.data + * @param {Dish} message.data.dish + * @param {number} message.data.id + */ + queueDishMessage(message) { + if (message.action === "getDishAs") { + this.dishWorkerQueue = [message].concat(this.dishWorkerQueue); + } else { + this.dishWorkerQueue.push(message); + } + } + + /** + * Sends a message to the DishWorker + * + * @param {object} message + * @param {string} message.action + * @param {object} message.data + */ + postDishMessage(message) { + if (this.dishWorker.currentAction !== "") { + this.queueDishMessage(message); + } else { + this.dishWorker.currentAction = message.action; + this.dishWorker.worker.postMessage(message); + } + } + + /** + * Sets the console log level in the workers. + */ + setLogLevel() { + for (let i = 0; i < this.chefWorkers.length; i++) { + this.chefWorkers[i].worker.postMessage({ + action: "setLogLevel", + data: log.getLevel() + }); + } + } + + /** + * Display the bake progress in the output bar and bake button + */ + displayProgress() { + const progress = this.getBakeProgress(); + if (progress.total === progress.baked) return; + + const percentComplete = ((progress.pending + progress.baking) / progress.total) * 100; + const bakeButton = document.getElementById("bake"); + if (this.app.baking) { + if (percentComplete < 100) { + bakeButton.style.background = `linear-gradient(to left, #fea79a ${percentComplete}%, #f44336 ${percentComplete}%)`; + } else { + bakeButton.style.background = ""; + } + } else { + // not baking + bakeButton.style.background = ""; + } + + const bakeInfo = document.getElementById("bake-info"); + if (progress.total > 1) { + let width = progress.total.toLocaleString().length; + width = width < 2 ? 2 : width; + + const totalStr = progress.total.toLocaleString().padStart(width, " ").replace(/ /g, " "); + const bakedStr = progress.baked.toLocaleString().padStart(width, " ").replace(/ /g, " "); + const pendingStr = progress.pending.toLocaleString().padStart(width, " ").replace(/ /g, " "); + const bakingStr = progress.baking.toLocaleString().padStart(width, " ").replace(/ /g, " "); + + let msg = "total: " + totalStr; + msg += "
    baked: " + bakedStr; + + if (progress.pending > 0) { + msg += "
    pending: " + pendingStr; + } else if (progress.baking > 0) { + msg += "
    baking: " + bakingStr; + } + bakeInfo.innerHTML = msg; + bakeInfo.style.display = ""; + } else { + bakeInfo.style.display = "none"; + } + + if (progress.total !== progress.baked) { + setTimeout(function() { + this.displayProgress(); + }.bind(this), 100); + } + + } + + /** + * Asks the ChefWorker to calculate highlight offsets if possible. + * + * @param {Object[]} recipeConfig + * @param {string} direction + * @param {Object} pos - The position object for the highlight. + * @param {number} pos.start - The start offset. + * @param {number} pos.end - The end offset. + */ + highlight(recipeConfig, direction, pos) { + let workerIdx = this.getInactiveChefWorker(false); + if (workerIdx === -1) { + workerIdx = this.addChefWorker(); + } + if (workerIdx === -1) return; + this.chefWorkers[workerIdx].worker.postMessage({ + action: "highlight", + data: { + recipeConfig: recipeConfig, + direction: direction, + pos: pos + } + }); + } +} + +export default WorkerWaiter; diff --git a/src/web/workers/DishWorker.mjs b/src/web/workers/DishWorker.mjs new file mode 100644 index 00000000..a171fdad --- /dev/null +++ b/src/web/workers/DishWorker.mjs @@ -0,0 +1,69 @@ +/** + * Web worker to handle dish conversion operations. + * + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Dish from "../../core/Dish.mjs"; + +self.addEventListener("message", function(e) { + // Handle message from the main thread + const r = e.data; + log.debug(`DishWorker receiving command '${r.action}'`); + + switch (r.action) { + case "getDishAs": + getDishAs(r.data); + break; + case "getDishTitle": + getDishTitle(r.data); + break; + default: + log.error(`DishWorker sent invalid action: '${r.action}'`); + } +}); + +/** + * Translates the dish to a given type + * + * @param {object} data + * @param {Dish} data.dish + * @param {string} data.type + * @param {number} data.id + */ +async function getDishAs(data) { + const newDish = new Dish(data.dish), + value = await newDish.get(data.type), + transferable = (data.type === "ArrayBuffer") ? [value] : undefined; + + self.postMessage({ + action: "dishReturned", + data: { + value: value, + id: data.id + } + }, transferable); +} + +/** + * Gets the title of the given dish + * + * @param {object} data + * @param {Dish} data.dish + * @param {number} data.id + * @param {number} data.maxLength + */ +async function getDishTitle(data) { + const newDish = new Dish(data.dish), + title = await newDish.getTitle(data.maxLength); + + self.postMessage({ + action: "dishReturned", + data: { + value: title, + id: data.id + } + }); +} diff --git a/src/web/workers/InputWorker.mjs b/src/web/workers/InputWorker.mjs new file mode 100644 index 00000000..cec68627 --- /dev/null +++ b/src/web/workers/InputWorker.mjs @@ -0,0 +1,1060 @@ +/** + * Web worker to handle the inputs. + * Handles storage, modification and retrieval of the inputs. + * + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Utils from "../../core/Utils.mjs"; +import {detectFileType} from "../../core/lib/FileType.mjs"; + +// Default max values +// These will be correctly calculated automatically +self.maxWorkers = 4; +self.maxTabs = 1; + +self.pendingFiles = []; +self.inputs = {}; +self.loaderWorkers = []; +self.currentInputNum = 1; +self.numInputs = 0; +self.pendingInputs = 0; +self.loadingInputs = 0; + +/** + * Respond to message from parent thread. + * + * @param {MessageEvent} e + */ +self.addEventListener("message", function(e) { + const r = e.data; + if (!("action" in r)) { + log.error("No action"); + return; + } + + log.debug(`Receiving ${r.action} from InputWaiter.`); + + switch (r.action) { + case "loadUIFiles": + self.loadFiles(r.data); + break; + case "loaderWorkerReady": + self.loaderWorkerReady(r.data); + break; + case "updateMaxWorkers": + self.maxWorkers = r.data; + break; + case "updateMaxTabs": + self.updateMaxTabs(r.data.maxTabs, r.data.activeTab); + break; + case "updateInputValue": + self.updateInputValue(r.data); + break; + case "updateInputObj": + self.updateInputObj(r.data); + break; + case "updateInputProgress": + self.updateInputProgress(r.data); + break; + case "bakeAll": + self.bakeAllInputs(); + break; + case "bakeNext": + self.bakeInput(r.data.inputNum, r.data.bakeId); + break; + case "getLoadProgress": + self.getLoadProgress(r.data); + break; + case "setInput": + self.setInput(r.data); + break; + case "setLogLevel": + log.setLevel(r.data, false); + break; + case "addInput": + self.addInput(r.data, "string"); + break; + case "refreshTabs": + self.refreshTabs(r.data.inputNum, r.data.direction); + break; + case "removeInput": + self.removeInput(r.data); + break; + case "changeTabRight": + self.changeTabRight(r.data.activeTab); + break; + case "changeTabLeft": + self.changeTabLeft(r.data.activeTab); + break; + case "autobake": + self.autoBake(r.data.activeTab, 0, false); + break; + case "filterTabs": + self.filterTabs(r.data); + break; + case "loaderWorkerMessage": + self.handleLoaderMessage(r.data); + break; + case "inputSwitch": + self.inputSwitch(r.data); + break; + case "updateTabHeader": + self.updateTabHeader(r.data); + break; + case "step": + self.autoBake(r.data.activeTab, r.data.progress, true); + break; + case "getInput": + self.getInput(r.data); + break; + case "getInputNums": + self.getInputNums(r.data); + break; + default: + log.error(`Unknown action '${r.action}'.`); + } +}); + +/** + * Gets the load progress of the input files, and the + * load progress for the input given in inputNum + * + * @param {number} inputNum - The input to get the file loading progress for + */ +self.getLoadProgress = function(inputNum) { + const total = self.numInputs; + const pending = self.pendingFiles.length; + const loading = self.loadingInputs; + const loaded = total - pending - loading; + + self.postMessage({ + action: "loadingInfo", + data: { + pending: pending, + loading: loading, + loaded: loaded, + total: total, + activeProgress: { + inputNum: inputNum, + progress: self.getInputProgress(inputNum) + } + } + }); +}; + +/** + * Fired when an autobake is initiated. + * Queues the active input and sends a bake command. + * + * @param {number} inputNum - The input to be baked + * @param {number} progress - The current progress of the bake through the recipe + * @param {boolean} [step=false] - Set to true if we should only execute one operation instead of the + * whole recipe + */ +self.autoBake = function(inputNum, progress, step=false) { + const input = self.getInputObj(inputNum); + if (input) { + self.postMessage({ + action: "bakeAllInputs", + data: { + nums: [parseInt(inputNum, 10)], + step: step, + progress: progress + } + }); + } +}; + +/** + * Fired when we want to bake all inputs (bake button clicked) + * Sends a list of inputNums to the workerwaiter + */ +self.bakeAllInputs = function() { + const inputNums = Object.keys(self.inputs), + nums = []; + + for (let i = 0; i < inputNums.length; i++) { + if (self.inputs[inputNums[i]].status === "loaded") { + nums.push(parseInt(inputNums[i], 10)); + } + } + self.postMessage({ + action: "bakeAllInputs", + data: { + nums: nums, + step: false, + progress: 0 + } + }); +}; + +/** + * Gets the data for the provided inputNum and sends it to the WorkerWaiter + * + * @param {number} inputNum + * @param {number} bakeId + */ +self.bakeInput = function(inputNum, bakeId) { + const inputObj = self.getInputObj(inputNum); + if (inputObj === null || + inputObj === undefined || + inputObj.status !== "loaded") { + self.postMessage({ + action: "queueInputError", + data: { + inputNum: inputNum, + bakeId: bakeId + } + }); + return; + } + + let inputData = inputObj.data; + if (typeof inputData !== "string") inputData = inputData.fileBuffer; + + self.postMessage({ + action: "queueInput", + data: { + input: inputData, + inputNum: inputNum, + bakeId: bakeId + } + }); +}; + +/** + * Gets the stored object for a specific inputNum + * + * @param {number} inputNum - The input we want to get the object for + * @returns {object} + */ +self.getInputObj = function(inputNum) { + return self.inputs[inputNum]; +}; + +/** + * Gets the stored value for a specific inputNum. + * + * @param {number} inputNum - The input we want to get the value of + * @returns {string | ArrayBuffer} + */ +self.getInputValue = function(inputNum) { + if (self.inputs[inputNum]) { + if (typeof self.inputs[inputNum].data === "string") { + return self.inputs[inputNum].data; + } else { + return self.inputs[inputNum].data.fileBuffer; + } + } + return ""; +}; + +/** + * Gets the stored value or object for a specific inputNum and sends it to the inputWaiter. + * + * @param {object} inputData - Object containing data about the input to retrieve + * @param {number} inputData.inputNum - The inputNum of the input to get + * @param {boolean} inputData.getObj - If true, returns the entire input object instead of just the value + * @param {number} inputData.id - The callback ID for the callback to run when returned to the inputWaiter + */ +self.getInput = function(inputData) { + const inputNum = inputData.inputNum, + data = (inputData.getObj) ? self.getInputObj(inputNum) : self.getInputValue(inputNum); + self.postMessage({ + action: "getInput", + data: { + data: data, + id: inputData.id + } + }); +}; + +/** + * Gets a list of the stored inputNums, along with the minimum and maximum + * + * @param {number} id - The callback ID to be executed when returned to the inputWaiter + */ +self.getInputNums = function(id) { + const inputNums = Object.keys(self.inputs), + min = self.getSmallestInputNum(inputNums), + max = self.getLargestInputNum(inputNums); + + self.postMessage({ + action: "getInputNums", + data: { + inputNums: inputNums, + min: min, + max: max, + id: id + } + }); +}; + +/** + * Gets the load progress for a specific inputNum + * + * @param {number} inputNum - The input we want to get the progress of + * @returns {number | string} - Returns "error" if there was a load error + */ +self.getInputProgress = function(inputNum) { + const inputObj = self.getInputObj(inputNum); + if (inputObj === undefined || inputObj === null) return; + if (inputObj.status === "error") { + return "error"; + } + return inputObj.progress; +}; + +/** + * Gets the largest inputNum of all the inputs + * + * @param {string[]} inputNums - The numbers to find the largest of + * @returns {number} + */ +self.getLargestInputNum = function(inputNums) { + return inputNums.reduce((acc, val) => { + val = parseInt(val, 10); + return val > acc ? val : acc; + }, -1); +}; + +/** + * Gets the smallest inputNum of all the inputs + * + * @param {string[]} inputNums - The numbers to find the smallest of + * @returns {number} + */ +self.getSmallestInputNum = function(inputNums) { + const min = inputNums.reduce((acc, val) => { + val = parseInt(val, 10); + return val < acc ? val : acc; + }, Number.MAX_SAFE_INTEGER); + + // Assume we don't have this many tabs! + if (min === Number.MAX_SAFE_INTEGER) return -1; + + return min; +}; + +/** + * Gets the next smallest inputNum + * + * @param {number} inputNum - The current input number + * @returns {number} + */ +self.getPreviousInputNum = function(inputNum) { + const inputNums = Object.keys(self.inputs); + if (inputNums.length === 0) return -1; + + return inputNums.reduce((acc, val) => { + val = parseInt(val, 10); + return (val < inputNum && val > acc) ? val : acc; + }, self.getSmallestInputNum(inputNums)); +}; + +/** + * Gets the next largest inputNum + * + * @param {number} inputNum - The current input number + * @returns {number} + */ +self.getNextInputNum = function(inputNum) { + const inputNums = Object.keys(self.inputs); + + return inputNums.reduce((acc, val) => { + val = parseInt(val, 10); + return (val > inputNum && val < acc) ? val : acc; + }, self.getLargestInputNum(inputNums)); +}; + +/** + * Gets a list of inputNums starting from the provided inputNum. + * If direction is "left", gets the inputNums higher than the provided number. + * If direction is "right", gets the inputNums lower than the provided number. + * @param {number} inputNum - The inputNum we want to get the neighbours of + * @param {string} direction - Either "left" or "right". Determines which direction we search for nearby numbers in + * @returns {number[]} + */ +self.getNearbyNums = function(inputNum, direction) { + const nums = []; + for (let i = 0; i < self.maxTabs; i++) { + let newNum; + if (i === 0 && self.inputs[inputNum] !== undefined) { + newNum = inputNum; + } else { + switch (direction) { + case "left": + newNum = self.getNextInputNum(nums[i - 1]); + if (newNum === nums[i - 1]) { + direction = "right"; + newNum = self.getPreviousInputNum(nums[0]); + } + break; + case "right": + newNum = self.getPreviousInputNum(nums[i - 1]); + if (newNum === nums[i - 1]) { + direction = "left"; + newNum = self.getNextInputNum(nums[0]); + } + } + } + if (!nums.includes(newNum) && (newNum > 0)) { + nums.push(newNum); + } + } + nums.sort(function(a, b) { + return a - b; + }); + return nums; +}; + +/** + * Gets the data to display in the tab header for an input, and + * posts it back to the inputWaiter + * + * @param {number} inputNum - The inputNum of the tab header + */ +self.updateTabHeader = function(inputNum) { + const input = self.getInputObj(inputNum); + if (input === null || input === undefined) return; + let inputData = input.data; + if (typeof inputData !== "string") { + inputData = input.data.name; + } + inputData = inputData.replace(/[\n\r]/g, ""); + + self.postMessage({ + action: "updateTabHeader", + data: { + inputNum: inputNum, + input: inputData.slice(0, 100) + } + }); +}; + +/** + * Gets the input for a specific inputNum, and posts it to the inputWaiter + * so that it can be displayed in the input area + * + * @param {object} inputData + * @param {number} inputData.inputNum - The input to get the data for + * @param {boolean} inputData.silent - If false, the manager statechange event won't be fired + */ +self.setInput = function(inputData) { + const inputNum = inputData.inputNum; + const silent = inputData.silent; + const input = self.getInputObj(inputNum); + if (input === undefined || input === null) return; + + let inputVal = input.data; + const inputObj = { + inputNum: inputNum, + input: inputVal + }; + if (typeof inputVal !== "string") { + inputObj.name = inputVal.name; + inputObj.size = inputVal.size; + inputObj.type = inputVal.type; + inputObj.progress = input.progress; + inputObj.status = input.status; + inputVal = inputVal.fileBuffer; + const fileSlice = inputVal.slice(0, 512001); + inputObj.input = fileSlice; + + self.postMessage({ + action: "setInput", + data: { + inputObj: inputObj, + silent: silent + } + }, [fileSlice]); + } else { + self.postMessage({ + action: "setInput", + data: { + inputObj: inputObj, + silent: silent + } + }); + } + self.updateTabHeader(inputNum); +}; + +/** + * Gets the nearby inputNums to the provided number, and posts them + * to the inputWaiter to be displayed on the page. + * + * @param {number} inputNum - The inputNum to find the nearby numbers for + * @param {string} direction - The direction to search for inputNums in. Either "left" or "right" + */ +self.refreshTabs = function(inputNum, direction) { + const nums = self.getNearbyNums(inputNum, direction), + inputNums = Object.keys(self.inputs), + tabsLeft = (self.getSmallestInputNum(inputNums) !== nums[0] && nums.length > 0), + tabsRight = (self.getLargestInputNum(inputNums) !== nums[nums.length - 1] && nums.length > 0); + + self.postMessage({ + action: "refreshTabs", + data: { + nums: nums, + activeTab: (nums.includes(inputNum)) ? inputNum : self.getNextInputNum(inputNum), + tabsLeft: tabsLeft, + tabsRight: tabsRight + } + }); + + // Update the tab headers for the new tabs + for (let i = 0; i < nums.length; i++) { + self.updateTabHeader(nums[i]); + } +}; + +/** + * Update the stored status for an input + * + * @param {number} inputNum - The input that's having its status changed + * @param {string} status - The status of the input + */ +self.updateInputStatus = function(inputNum, status) { + if (self.inputs[inputNum] !== undefined) { + self.inputs[inputNum].status = status; + } +}; + +/** + * Update the stored load progress of an input + * + * @param {object} inputData + * @param {number} inputData.inputNum - The input that's having its progress updated + * @param {number} inputData.progress - The load progress of the input + */ +self.updateInputProgress = function(inputData) { + const inputNum = inputData.inputNum; + const progress = inputData.progress; + + if (self.inputs[inputNum] !== undefined) { + self.inputs[inputNum].progress = progress; + } +}; + +/** + * Update the stored value of an input. + * + * @param {object} inputData + * @param {number} inputData.inputNum - The input that's having its value updated + * @param {string | ArrayBuffer} inputData.value - The new value of the input + * @param {boolean} inputData.force - If true, still updates the input value if the input type is different to the stored value + */ +self.updateInputValue = function(inputData) { + const inputNum = inputData.inputNum; + if (inputNum < 1) return; + if (Object.prototype.hasOwnProperty.call(self.inputs[inputNum].data, "fileBuffer") && + typeof inputData.value === "string" && !inputData.force) return; + const value = inputData.value; + if (self.inputs[inputNum] !== undefined) { + if (typeof value === "string") { + self.inputs[inputNum].data = value; + } else { + self.inputs[inputNum].data.fileBuffer = value; + } + self.inputs[inputNum].status = "loaded"; + self.inputs[inputNum].progress = 100; + return; + } + + // If we get to here, an input for inputNum could not be found, + // so create a new one. Only do this if the value is a string, as + // loadFiles will create the input object for files + if (typeof value === "string") { + self.inputs.push({ + inputNum: inputNum, + data: value, + status: "loaded", + progress: 100 + }); + } +}; + +/** + * Update the stored data object for an input. + * Used if we need to change a string to an ArrayBuffer + * + * @param {object} inputData + * @param {number} inputData.inputNum - The number of the input we're updating + * @param {object} inputData.data - The new data object for the input + */ +self.updateInputObj = function(inputData) { + const inputNum = inputData.inputNum; + const data = inputData.data; + + if (self.getInputObj(inputNum) === -1) return; + + self.inputs[inputNum].data = data; +}; + +/** + * Get the index of a loader worker object. + * Returns -1 if the worker could not be found + * + * @param {number} workerId - The ID of the worker we're searching for + * @returns {number} + */ +self.getLoaderWorkerIdx = function(workerId) { + for (let i = 0; i < self.loaderWorkers.length; i++) { + if (self.loaderWorkers[i].id === workerId) { + return i; + } + } + return -1; +}; + +/** + * Fires when a loaderWorker is ready to load files. + * Stores data about the new loaderWorker in the loaderWorkers array, + * and sends the next file to the loaderWorker to be loaded. + * + * @param {object} workerData + * @param {number} workerData.id - The ID of the new loaderWorker + */ +self.loaderWorkerReady = function(workerData) { + const newWorkerObj = { + id: workerData.id, + inputNum: -1, + active: true + }; + self.loaderWorkers.push(newWorkerObj); + self.loadNextFile(self.loaderWorkers.indexOf(newWorkerObj)); +}; + +/** + * Handler for messages sent by loaderWorkers. + * (Messages are sent between the inputWorker and + * loaderWorkers via the main thread) + * + * @param {object} r - The data sent by the loaderWorker + * @param {number} r.inputNum - The inputNum which the message corresponds to + * @param {string} r.error - Present if an error is fired by the loaderWorker. Contains the error message string. + * @param {ArrayBuffer} r.fileBuffer - Present if a file has finished loading. Contains the loaded file buffer. + */ +self.handleLoaderMessage = function(r) { + let inputNum = 0; + + if ("inputNum" in r) { + inputNum = r.inputNum; + } + + if ("error" in r) { + self.updateInputProgress(r.inputNum, 0); + self.updateInputStatus(r.inputNum, "error"); + + log.error(r.error); + self.loadingInputs--; + + self.terminateLoaderWorker(r.id); + self.activateLoaderWorker(); + + self.setInput({inputNum: inputNum, silent: true}); + return; + } + + if ("fileBuffer" in r) { + log.debug(`Input file ${inputNum} loaded.`); + self.loadingInputs--; + self.updateInputValue({ + inputNum: inputNum, + value: r.fileBuffer + }); + + const idx = self.getLoaderWorkerIdx(r.id); + self.loadNextFile(idx); + } else if ("progress" in r) { + self.updateInputProgress(r); + } +}; + +/** + * Loads the next file using a loaderWorker + * + * @param {number} - The loaderWorker which will load the file + */ +self.loadNextFile = function(workerIdx) { + if (workerIdx === -1) return; + const id = self.loaderWorkers[workerIdx].id; + if (self.pendingFiles.length === 0) { + const workerObj = self.loaderWorkers.splice(workerIdx, 1)[0]; + self.terminateLoaderWorker(workerObj.id); + return; + } + + const nextFile = self.pendingFiles.splice(0, 1)[0]; + self.loaderWorkers[workerIdx].inputNum = nextFile.inputNum; + self.loadingInputs++; + self.postMessage({ + action: "loadInput", + data: { + file: nextFile.file, + inputNum: nextFile.inputNum, + workerId: id + } + }); +}; + +/** + * Sends a message to the inputWaiter to create a new loaderWorker. + * If there's an inactive loaderWorker that already exists, use that instead. + */ +self.activateLoaderWorker = function() { + for (let i = 0; i < self.loaderWorkers.length; i++) { + if (!self.loaderWorkers[i].active) { + self.loaderWorkers[i].active = true; + self.loadNextFile(i); + return; + } + } + self.postMessage({ + action: "activateLoaderWorker" + }); +}; + +/** + * Sends a message to the inputWaiter to terminate a loaderWorker. + * + * @param {number} id - The ID of the worker to be terminated + */ +self.terminateLoaderWorker = function(id) { + self.postMessage({ + action: "terminateLoaderWorker", + data: id + }); + // If we still have pending files, spawn a worker + if (self.pendingFiles.length > 0) { + self.activateLoaderWorker(); + } +}; + +/** + * Loads files using LoaderWorkers + * + * @param {object} filesData + * @param {FileList} filesData.files - The list of files to be loaded + * @param {number} filesData.activeTab - The active tab in the UI + */ +self.loadFiles = function(filesData) { + const files = filesData.files; + const activeTab = filesData.activeTab; + let lastInputNum = -1; + const inputNums = []; + for (let i = 0; i < files.length; i++) { + if (i === 0 && self.getInputValue(activeTab) === "") { + self.removeInput({ + inputNum: activeTab, + refreshTabs: false, + removeChefWorker: false + }); + lastInputNum = self.addInput(false, "file", { + name: files[i].name, + size: files[i].size.toLocaleString(), + type: files[i].type || "unknown" + }, activeTab); + } else { + lastInputNum = self.addInput(false, "file", { + name: files[i].name, + size: files[i].size.toLocaleString(), + type: files[i].type || "unknown" + }); + } + inputNums.push(lastInputNum); + + self.pendingFiles.push({ + file: files[i], + inputNum: lastInputNum + }); + } + let max = self.maxWorkers; + if (self.pendingFiles.length < self.maxWorkers) max = self.pendingFiles.length; + + // Create loaderWorkers to load the new files + for (let i = 0; i < max; i++) { + self.activateLoaderWorker(); + } + + self.getLoadProgress(); + self.setInput({inputNum: activeTab, silent: false}); +}; + +/** + * Adds an input to the input dictionary + * + * @param {boolean} [changetab=false] - Whether or not to change to the new input + * @param {string} type - Either "string" or "file" + * @param {Object} fileData - Contains information about the file to be added to the input (only used when type is "file") + * @param {string} fileData.name - The filename of the input being added + * @param {number} fileData.size - The file size (in bytes) of the input being added + * @param {string} fileData.type - The MIME type of the input being added + * @param {number} inputNum - Defaults to auto-incrementing self.currentInputNum + */ +self.addInput = function( + changeTab = false, + type, + fileData = { + name: "unknown", + size: "unknown", + type: "unknown" + }, + inputNum = self.currentInputNum++ +) { + self.numInputs++; + const newInputObj = { + inputNum: inputNum + }; + + switch (type) { + case "string": + newInputObj.data = ""; + newInputObj.status = "loaded"; + newInputObj.progress = 100; + break; + case "file": + newInputObj.data = { + fileBuffer: new ArrayBuffer(), + name: fileData.name, + size: fileData.size, + type: fileData.type + }; + newInputObj.status = "pending"; + newInputObj.progress = 0; + break; + default: + log.error(`Invalid type '${type}'.`); + return -1; + } + self.inputs[inputNum] = newInputObj; + + // Tell the inputWaiter we've added an input, so it can create a tab to display it + self.postMessage({ + action: "inputAdded", + data: { + changeTab: changeTab, + inputNum: inputNum + } + }); + + return inputNum; +}; + +/** + * Remove an input from the inputs dictionary + * + * @param {object} removeInputData + * @param {number} removeInputData.inputNum - The number of the input to be removed + * @param {boolean} removeInputData.refreshTabs - If true, refresh the tabs after removing the input + * @param {boolean} removeInputData.removeChefWorker - If true, remove a chefWorker from the WorkerWaiter + */ +self.removeInput = function(removeInputData) { + const inputNum = removeInputData.inputNum; + const refreshTabs = removeInputData.refreshTabs; + self.numInputs--; + + for (let i = 0; i < self.loaderWorkers.length; i++) { + if (self.loaderWorkers[i].inputNum === inputNum) { + // Terminate any loaderWorker that's loading the removed input + self.loadingInputs--; + self.terminateLoaderWorker(self.loaderWorkers[i].id); + break; + } + } + + for (let i = 0; i < self.pendingFiles.length; i++) { + // Remove the input from the pending files list + if (self.pendingFiles[i].inputNum === inputNum) { + self.pendingFiles.splice(i, 1); + break; + } + } + + delete self.inputs[inputNum]; + + if (refreshTabs) { + self.refreshTabs(self.getPreviousInputNum(inputNum), "left"); + } + + if (self.numInputs < self.maxWorkers && removeInputData.removeChefWorker) { + self.postMessage({ + action: "removeChefWorker" + }); + } +}; + +/** + * Change to the next tab. + * + * @param {number} inputNum - The inputNum of the tab to change to + */ +self.changeTabRight = function(inputNum) { + const newInput = self.getNextInputNum(inputNum); + self.postMessage({ + action: "changeTab", + data: newInput + }); +}; + +/** + * Change to the previous tab. + * + * @param {number} inputNum - The inputNum of the tab to change to + */ +self.changeTabLeft = function(inputNum) { + const newInput = self.getPreviousInputNum(inputNum); + self.postMessage({ + action: "changeTab", + data: newInput + }); +}; + +/** + * Updates the maximum number of tabs, and refreshes them if it changes + * + * @param {number} maxTabs - The new max number of tabs + * @param {number} activeTab - The currently selected tab + */ +self.updateMaxTabs = function(maxTabs, activeTab) { + if (self.maxTabs !== maxTabs) { + self.maxTabs = maxTabs; + self.refreshTabs(activeTab, "right"); + } +}; + +/** + * Search the inputs for any that match the filters provided, + * posting the results back to the inputWaiter + * + * @param {object} searchData - Object containing the search filters + * @param {boolean} searchData.showPending - If true, include pending inputs in the results + * @param {boolean} searchData.showLoading - If true, include loading inputs in the results + * @param {boolean} searchData.showLoaded - If true, include loaded inputs in the results + * @param {string} searchData.filter - A regular expression to match the inputs on + * @param {string} searchData.filterType - Either "CONTENT" or "FILENAME". Detemines what should be matched with filter + * @param {number} searchData.numResults - The maximum number of results to be returned + */ +self.filterTabs = function(searchData) { + const showPending = searchData.showPending, + showLoading = searchData.showLoading, + showLoaded = searchData.showLoaded, + filterType = searchData.filterType; + + let filterExp; + try { + filterExp = new RegExp(searchData.filter, "i"); + } catch (error) { + self.postMessage({ + action: "filterTabError", + data: error.message + }); + return; + } + const numResults = searchData.numResults; + + const inputs = []; + const inputNums = Object.keys(self.inputs); + for (let i = 0; i < inputNums.length; i++) { + const iNum = inputNums[i]; + let textDisplay = ""; + let addInput = false; + if (self.inputs[iNum].status === "pending" && showPending || + self.inputs[iNum].status === "loading" && showLoading || + self.inputs[iNum].status === "loaded" && showLoaded) { + try { + if (typeof self.inputs[iNum].data === "string") { + if (filterType.toLowerCase() === "content" && + filterExp.test(self.inputs[iNum].data.slice(0, 4096))) { + textDisplay = self.inputs[iNum].data.slice(0, 4096); + addInput = true; + } + } else { + if ((filterType.toLowerCase() === "filename" && + filterExp.test(self.inputs[iNum].data.name)) || + filterType.toLowerCase() === "content" && + filterExp.test(Utils.arrayBufferToStr(self.inputs[iNum].data.fileBuffer.slice(0, 4096)))) { + textDisplay = self.inputs[iNum].data.name; + addInput = true; + } + } + } catch (error) { + self.postMessage({ + action: "filterTabError", + data: error.message + }); + return; + } + } + + if (addInput) { + if (textDisplay === "" || textDisplay === undefined) { + textDisplay = "New Tab"; + } + const inputItem = { + inputNum: iNum, + textDisplay: textDisplay + }; + inputs.push(inputItem); + } + if (inputs.length >= numResults) { + break; + } + } + + // Send the results back to the inputWaiter + self.postMessage({ + action: "displayTabSearchResults", + data: inputs + }); +}; + +/** + * Swaps the input and outputs, and sends the old input back to the main thread. + * + * @param {object} switchData + * @param {number} switchData.inputNum - The inputNum of the input to be switched to + * @param {string | ArrayBuffer} switchData.outputData - The data to switch to + */ +self.inputSwitch = function(switchData) { + const currentInput = self.getInputObj(switchData.inputNum); + const currentData = currentInput.data; + if (currentInput === undefined || currentInput === null) return; + + if (typeof switchData.outputData === "object") { + const output = new Uint8Array(switchData.outputData), + types = detectFileType(output); + let type = "unknown", + ext = "dat"; + if (types.length) { + type = types[0].mime; + ext = types[0].extension.split(",", 1)[0]; + } + + // ArrayBuffer + currentInput.data = { + fileBuffer: switchData.outputData, + name: `output.${ext}`, + size: switchData.outputData.byteLength.toLocaleString(), + type: type + }; + } else { + // String + currentInput.data = switchData.outputData; + } + + self.postMessage({ + action: "inputSwitch", + data: { + data: currentData, + inputNum: switchData.inputNum + } + }); + + self.setInput({inputNum: switchData.inputNum, silent: false}); + +}; diff --git a/src/web/workers/LoaderWorker.js b/src/web/workers/LoaderWorker.js new file mode 100755 index 00000000..4c38998e --- /dev/null +++ b/src/web/workers/LoaderWorker.js @@ -0,0 +1,76 @@ +/** + * Web Worker to load large amounts of data without locking up the UI. + * + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ + +self.id = null; + + +self.handleMessage = function(e) { + const r = e.data; + log.debug(`LoaderWorker receiving command '${r.action}'`); + + switch (r.action) { + case "loadInput": + self.loadFile(r.data.file, r.data.inputNum); + break; + } +}; + + +/** + * Respond to message from parent thread. + */ +self.addEventListener("message", function(e) { + const r = e.data; + if (Object.prototype.hasOwnProperty.call(r, "file") && Object.prototype.hasOwnProperty.call(r, "inputNum")) { + self.loadFile(r.file, r.inputNum); + } else if (Object.prototype.hasOwnProperty.call(r, "file")) { + self.loadFile(r.file, ""); + } else if (Object.prototype.hasOwnProperty.call(r, "id")) { + self.id = r.id; + } +}); + + +/** + * Loads a file object into an ArrayBuffer, then transfers it back to the parent thread. + * + * @param {File} file + * @param {string} inputNum + */ +self.loadFile = function(file, inputNum) { + const reader = new FileReader(); + if (file.size >= 256*256*256*128) { + self.postMessage({"error": "File size too large.", "inputNum": inputNum, "id": self.id}); + return; + } + const data = new Uint8Array(file.size); + let offset = 0; + const CHUNK_SIZE = 10485760; // 10MiB + + const seek = function() { + if (offset >= file.size) { + self.postMessage({"fileBuffer": data.buffer, "inputNum": inputNum, "id": self.id}, [data.buffer]); + return; + } + self.postMessage({"progress": Math.round(offset / file.size * 100), "inputNum": inputNum}); + const slice = file.slice(offset, offset + CHUNK_SIZE); + reader.readAsArrayBuffer(slice); + }; + + reader.onload = function(e) { + data.set(new Uint8Array(reader.result), offset); + offset += CHUNK_SIZE; + seek(); + }; + + reader.onerror = function(e) { + self.postMessage({"error": reader.error.message, "inputNum": inputNum, "id": self.id}); + }; + + seek(); +}; diff --git a/src/web/workers/ZipWorker.mjs b/src/web/workers/ZipWorker.mjs new file mode 100644 index 00000000..85a8cb4c --- /dev/null +++ b/src/web/workers/ZipWorker.mjs @@ -0,0 +1,73 @@ +/** + * Web Worker to handle zipping the outputs for download. + * + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import zip from "zlibjs/bin/zip.min.js"; +import Utils from "../../core/Utils.mjs"; +import Dish from "../../core/Dish.mjs"; +import {detectFileType} from "../../core/lib/FileType.mjs"; + +const Zlib = zip.Zlib; + +/** + * Respond to message from parent thread. + */ +self.addEventListener("message", function(e) { + const r = e.data; + if (!("outputs" in r)) { + log.error("No files were passed to the ZipWorker."); + return; + } + if (!("filename" in r)) { + log.error("No filename was passed to the ZipWorker"); + return; + } + + self.zipFiles(r.outputs, r.filename, r.fileExtension); +}); + +self.setOption = function(...args) {}; + +/** + * Compress the files into a zip file and send the zip back + * to the OutputWaiter. + * + * @param {object} outputs + * @param {string} filename + * @param {string} fileExtension + */ +self.zipFiles = async function(outputs, filename, fileExtension) { + const zip = new Zlib.Zip(); + const inputNums = Object.keys(outputs); + + for (let i = 0; i < inputNums.length; i++) { + const iNum = inputNums[i]; + let ext = fileExtension; + + const cloned = new Dish(outputs[iNum].data.dish); + const output = new Uint8Array(await cloned.get(Dish.ARRAY_BUFFER)); + + if (fileExtension === undefined || fileExtension === "") { + // Detect automatically + const types = detectFileType(output); + if (!types.length) { + ext = ".dat"; + } else { + ext = `.${types[0].extension.split(",", 1)[0]}`; + } + } + const name = Utils.strToByteArray(iNum + ext); + + zip.addFile(output, {filename: name}); + } + + const zippedFile = zip.compress(); + self.postMessage({ + zippedFile: zippedFile.buffer, + filename: filename + }, [zippedFile.buffer]); +}; diff --git a/tests/browser/nightwatch.js b/tests/browser/nightwatch.js index 23100d8d..f966791b 100644 --- a/tests/browser/nightwatch.js +++ b/tests/browser/nightwatch.js @@ -82,6 +82,7 @@ module.exports = { browser .useCss() .setValue("#input-text", "Don't Panic.") + .pause(1000) .click("#bake"); // Check output diff --git a/tests/operations/TestRegister.mjs b/tests/lib/TestRegister.mjs similarity index 65% rename from tests/operations/TestRegister.mjs rename to tests/lib/TestRegister.mjs index f2b9f085..7b1be8c4 100644 --- a/tests/operations/TestRegister.mjs +++ b/tests/lib/TestRegister.mjs @@ -5,37 +5,49 @@ * ensure that they will get run by the frontend. * * @author tlwr [toby@toby.codes] - * @copyright Crown Copyright 2017 + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import Chef from "../../src/core/Chef"; +import Chef from "../../src/core/Chef.mjs"; + +/** + * Object to store and run the list of tests. + * + * @class + * @constructor + */ +class TestRegister { -(function() { /** - * Object to store and run the list of tests. - * - * @class - * @constructor + * initialise with no tests */ - function TestRegister() { + constructor() { this.tests = []; + this.apiTests = []; } - /** * Add a list of tests to the register. * * @param {Object[]} tests */ - TestRegister.prototype.addTests = function(tests) { + addTests(tests) { this.tests = this.tests.concat(tests); - }; + } + /** + * Add a list of api tests to the register + * @param {Object[]} tests + */ + addApiTests(tests) { + this.apiTests = this.apiTests.concat(tests); + } /** * Runs all the tests in the register. */ - TestRegister.prototype.runTests = function() { + runTests () { return Promise.all( this.tests.map(function(test, i) { const chef = new Chef(); @@ -66,7 +78,7 @@ import Chef from "../../src/core/Chef"; ret.output = "Expected an error but did not receive one."; } else if (result.result === test.expectedOutput) { ret.status = "passing"; - } else if (test.hasOwnProperty("expectedMatch") && test.expectedMatch.test(result.result)) { + } else if ("expectedMatch" in test && test.expectedMatch.test(result.result)) { ret.status = "passing"; } else { ret.status = "failing"; @@ -85,12 +97,29 @@ import Chef from "../../src/core/Chef"; }); }) ); - }; + } + /** + * Run all api related tests and wrap results in report format + */ + runApiTests() { + return Promise.all(this.apiTests.map(async function(test, i) { + const result = { + test: test, + status: null, + output: null, + }; + try { + await test.run(); + result.status = "passing"; + } catch (e) { + result.status = "erroring"; + result.output = e.message; + } + return result; + })); + } +} - // Singleton TestRegister, keeping things simple and obvious. - global.TestRegister = global.TestRegister || new TestRegister(); -})(); - -export default global.TestRegister; - +// Export an instance to make a singleton +export default new TestRegister(); diff --git a/tests/lib/utils.mjs b/tests/lib/utils.mjs new file mode 100644 index 00000000..c04290b2 --- /dev/null +++ b/tests/lib/utils.mjs @@ -0,0 +1,84 @@ +/** + * Utils for test suite + * + * @author d98762625@gmail.com + * @author tlwr [toby@toby.codes] + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + + +/** + * Helper function to convert a status to an icon. + * + * @param {string} status + * @returns {string} + */ +const statusToIcon = function statusToIcon(status) { + const icons = { + erroring: "🔥", + failing: "❌", + passing: "✔️️", + }; + return icons[status] || "?"; +}; + + +/** + * Displays a given test result in the console. + * Counts test statuses. + * + * @param {Object} testStatusCounts + * @param {Object} testResult + */ +function handleTestResult(testStatus, testResult) { + testStatus.allTestsPassing = testStatus.allTestsPassing && testResult.status === "passing"; + const newCount = (testStatus.counts[testResult.status] || 0) + 1; + testStatus.counts[testResult.status] = newCount; + testStatus.counts.total += 1; + console.log([ + statusToIcon(testResult.status), + testResult.test.name + ].join(" ")); + + if (testResult.output) { + console.log( + testResult.output + .trim() + .replace(/^/, "\t") + .replace(/\n/g, "\n\t") + ); + } +} + +/** + * Log each test result, count tests and failures. Log test suite run duration. + * + * @param {Object} testStatus - object describing test run data + * @param {Object[]} results - results from TestRegister + */ +export function logTestReport(testStatus, results) { + results.forEach(r => handleTestResult(testStatus, r)); + console.log("\n"); + + for (const testStatusCount in testStatus.counts) { + const count = testStatus.counts[testStatusCount]; + if (count > 0) { + console.log(testStatusCount.toUpperCase(), count); + } + } + + process.exit(testStatus.allTestsPassing ? 0 : 1); +} + +/** + * Fail if the process takes longer than 60 seconds. + */ +export function setLongTestFailure() { + setTimeout(function() { + console.log("Tests took longer than 60 seconds to run, returning."); + process.exit(1); + }, 60 * 1000); +} + diff --git a/tests/node/assertionHandler.mjs b/tests/node/assertionHandler.mjs new file mode 100644 index 00000000..0096db42 --- /dev/null +++ b/tests/node/assertionHandler.mjs @@ -0,0 +1,60 @@ +/** + * assertionHandler.mjs + * + * Pair native node assertions with a description for + * the benefit of the TestRegister. + * + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +/* eslint no-console: 0 */ + + +/** + * Print useful stack on error + */ +const wrapRun = (run) => () => { + try { + run(); + } catch (e) { + console.dir(e); + throw e; + } +}; + + +/** + * it - wrapper for assertions to provide a helpful description + * to the TestRegister + * @namespace ApiTests + * @param {String} description - The description of the test + * @param {Function} assertion - The test + * + * @example + * // One assertion + * it("should run one assertion", () => assert.equal(1,1)) + * + * @example + * // multiple assertions + * it("should handle multiple assertions", () => { + * assert.equal(1,1) + * assert.notEqual(3,4) + * }) + * + * @example + * // async assertions + * it("should handle async", async () => { + * let r = await asyncFunc() + * assert(r) + * }) + */ +export function it(name, run) { + return { + name: `Node API: ${name}`, + run: wrapRun(run), + }; +} + +export default it; diff --git a/tests/node/consumers/cjs-consumer.js b/tests/node/consumers/cjs-consumer.js new file mode 100644 index 00000000..16232312 --- /dev/null +++ b/tests/node/consumers/cjs-consumer.js @@ -0,0 +1,29 @@ +/** + * Tests to ensure that a consuming app can use CJS require + * + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +const chef = require("cyberchef"); +const assert = require("assert"); + +const d = chef.bake("Testing, 1 2 3", [ + chef.toHex, + chef.reverse, + { + op: chef.unique, + args: { + delimiter: "Space", + } + }, + { + op: chef.multiply, + args: { + delimiter: "Space", + } + } +]); + +assert.equal(d.value, "630957449041920"); diff --git a/tests/node/consumers/esm-consumer.mjs b/tests/node/consumers/esm-consumer.mjs new file mode 100644 index 00000000..3536ef00 --- /dev/null +++ b/tests/node/consumers/esm-consumer.mjs @@ -0,0 +1,28 @@ +/** + * Tests to ensure that a consuming app can use ESM imports + * + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ +import assert from "assert"; +import chef from "cyberchef"; + +const d = chef.bake("Testing, 1 2 3", [ + chef.toHex, + chef.reverse, + { + op: chef.unique, + args: { + delimiter: "Space", + } + }, + { + op: chef.multiply, + args: { + delimiter: "Space", + } + } +]); + +assert.equal(d.value, "630957449041920"); diff --git a/tests/node/consumers/esm-deep-import-consumer.mjs b/tests/node/consumers/esm-deep-import-consumer.mjs new file mode 100644 index 00000000..58fd921a --- /dev/null +++ b/tests/node/consumers/esm-deep-import-consumer.mjs @@ -0,0 +1,28 @@ +/** + * Tests to ensure that a consuming app can use named imports from deep import patch + * + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ +import assert from "assert"; +import { bake, toHex, reverse, unique, multiply } from "cyberchef/src/node/index.mjs"; + +const d = bake("Testing, 1 2 3", [ + toHex, + reverse, + { + op: unique, + args: { + delimiter: "Space", + } + }, + { + op: multiply, + args: { + delimiter: "Space", + } + } +]); + +assert.equal(d.value, "630957449041920"); diff --git a/tests/node/index.mjs b/tests/node/index.mjs new file mode 100644 index 00000000..056db3d2 --- /dev/null +++ b/tests/node/index.mjs @@ -0,0 +1,37 @@ +/* eslint no-console: 0 */ + +/** + * Node API Test Runner + * + * @author d98762625 [d98762625@gmail.com] + * @author tlwr [toby@toby.codes] + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import { + setLongTestFailure, + logTestReport, +} from "../lib/utils"; + +import TestRegister from "../lib/TestRegister.mjs"; +import "./tests/nodeApi"; +import "./tests/operations"; +import "./tests/File"; +import "./tests/NodeDish"; + +const testStatus = { + allTestsPassing: true, + counts: { + total: 0, + } +}; + +setLongTestFailure(); + +const logOpsTestReport = logTestReport.bind(null, testStatus); + +TestRegister.runApiTests() + .then(logOpsTestReport); + diff --git a/tests/node/sampleData/pic.jpg b/tests/node/sampleData/pic.jpg new file mode 100644 index 00000000..05c21327 Binary files /dev/null and b/tests/node/sampleData/pic.jpg differ diff --git a/tests/node/tests/File.mjs b/tests/node/tests/File.mjs new file mode 100644 index 00000000..c6a1e60a --- /dev/null +++ b/tests/node/tests/File.mjs @@ -0,0 +1,82 @@ +import assert from "assert"; +import it from "../assertionHandler.mjs"; +import TestRegister from "../../lib/TestRegister.mjs"; +import File from "../../../src/node/File.mjs"; +import {zip, Dish} from "../../../src/node/index.mjs"; + +TestRegister.addApiTests([ + it("File: should exist", () => { + assert(File); + }), + + it("File: Should have same properties as DOM File object", () => { + const uint8Array = new Uint8Array(Buffer.from("hello")); + const file = new File([uint8Array], "name.txt"); + assert.equal(file.name, "name.txt"); + assert(typeof file.lastModified, "number"); + assert(file.lastModifiedDate instanceof Date); + assert.equal(file.size, uint8Array.length); + assert.equal(file.type, "application/unknown"); + }), + + it("File: Should determine the type of a file", () => { + const zipped = zip("hello"); + const file = new File([zipped.value]); + assert(file); + assert.strictEqual(file.type, "application/zip"); + }), + + it("File: unknown type should have a type of application/unknown", () => { + const uint8Array = new Uint8Array(Buffer.from("hello")); + const file = new File([uint8Array], "sample.txt"); + assert.strictEqual(file.type, "application/unknown"); + }), + + it("File: should be able to make a dish from it", () => { + const uint8Array = new Uint8Array(Buffer.from("hello")); + const file = new File([uint8Array], "sample.txt"); + try { + const dish = new Dish(file, 7); + assert.ok(dish.valid()); + } catch (e) { + assert.fail(e.message); + } + }), + + it("File: should allow dish to translate to ArrayBuffer", () => { + const uint8Array = new Uint8Array(Buffer.from("hello")); + const file = new File([uint8Array], "sample.txt"); + try { + const dish = new Dish(file, 7); + assert.ok(dish.value); + + dish.get(4); + assert.strictEqual(dish.type, 4); + assert.ok(dish.valid()); + + } catch (e) { + assert.fail(e.message); + } + }), + + it("File: should allow dish to translate from ArrayBuffer to File", () => { + const uint8Array = new Uint8Array(Buffer.from("hello")); + const file = new File([uint8Array], "sample.txt"); + try { + const dish = new Dish(file, 7); + assert.ok(dish.value); + + // translate to ArrayBuffer + dish.get(4); + assert.ok(dish.valid()); + + // translate back to File + dish.get(7); + assert.ok(dish.valid()); + + } catch (e) { + assert.fail(e.message); + } + }) + +]); diff --git a/tests/node/tests/NodeDish.mjs b/tests/node/tests/NodeDish.mjs new file mode 100644 index 00000000..72120703 --- /dev/null +++ b/tests/node/tests/NodeDish.mjs @@ -0,0 +1,199 @@ +import assert from "assert"; +import it from "../assertionHandler.mjs"; +import fs from "fs"; + +import BigNumber from "bignumber.js"; + +import { Dish, toBase32, SHA3 } from "../../../src/node/index.mjs"; +import File from "../../../src/node/File.mjs"; +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addApiTests([ + it("Composable Dish: Should have top level Dish object", () => { + assert.ok(Dish); + }), + + it("Composable Dish: Should construct empty dish object", () => { + const dish = new Dish(); + assert.strictEqual(dish.value.byteLength, new ArrayBuffer(0).byteLength); + assert.strictEqual(dish.type, 4); + }), + + it("Composable Dish: constructed dish should have apply prototype functions", () => { + const dish = new Dish(); + assert.ok(dish.apply); + assert.throws(() => dish.someInvalidFunction()); + }), + + it("Composable Dish: composed function returns another dish", () => { + const result = new Dish("some input").apply(toBase32); + assert.ok(result instanceof Dish); + }), + + + it("Composable dish: infers type from input if needed", () => { + const dish = new Dish("string input"); + assert.strictEqual(dish.type, 1); + + const numberDish = new Dish(333); + assert.strictEqual(numberDish.type, 2); + + const arrayBufferDish = new Dish(Buffer.from("some buffer input").buffer); + assert.strictEqual(arrayBufferDish.type, 4); + + const byteArrayDish = new Dish(Buffer.from("some buffer input")); + assert.strictEqual(byteArrayDish.type, 0); + + const JSONDish = new Dish({key: "value"}); + assert.strictEqual(JSONDish.type, 6); + }), + + it("Composable dish: Buffer type dishes should be converted to strings", () => { + fs.writeFileSync("test.txt", "abc"); + const dish = new Dish(fs.readFileSync("test.txt")); + assert.strictEqual(dish.type, 0); + fs.unlinkSync("test.txt"); + }), + + it("Composable Dish: apply should allow set of arguments for operation", () => { + const result = new Dish("input").apply(SHA3, {size: "256"}); + assert.strictEqual(result.toString(), "7640cc9b7e3662b2250a43d1757e318bb29fb4860276ac4373b67b1650d6d3e3"); + }), + + it("Composable Dish: apply functions can be chained", () => { + const result = new Dish("input").apply(toBase32).apply(SHA3, {size: "224"}); + assert.strictEqual(result.toString(), "493e8136b759370a415ef2cf2f7a69690441ff86592aba082bc2e2e0"); + }), + + it("Dish translation: ArrayBuffer to ArrayBuffer", () => { + const dish = new Dish(new ArrayBuffer(10), 4); + dish.get("array buffer"); + assert.strictEqual(dish.value.byteLength, 10); + assert.strictEqual(dish.type, 4); + }), + + it("Dish translation: ArrayBuffer and String", () => { + const dish = new Dish("some string", 1); + dish.get("array buffer"); + + assert.strictEqual(dish.type, 4); + assert.deepStrictEqual(dish.value, new Uint8Array([0x73, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67]).buffer); + assert.deepEqual(dish.value.byteLength, 11); + + dish.get("string"); + assert.strictEqual(dish.type, 1); + assert.strictEqual(dish.value, "some string"); + }), + + it("Dish translation: ArrayBuffer and number", () => { + const dish = new Dish(100, 2); + dish.get(4); + + assert.strictEqual(dish.type, 4); + assert.deepStrictEqual(dish.value, new Uint8Array([0x31, 0x30, 0x30]).buffer); + assert.strictEqual(dish.value.byteLength, 3); + + // Check the data in ArrayBuffer represents 100 as a string. + const view = new DataView(dish.value, 0); + assert.strictEqual(String.fromCharCode(view.getUint8(0), view.getUint8(1), view.getUint8(2)), "100"); + + dish.get("number"); + assert.strictEqual(dish.type, 2); + assert.strictEqual(dish.value, 100); + }), + + it("Dish translation: ArrayBuffer and byte array", () => { + const dish = new Dish(new Uint8Array([1, 2, 3]), 0); + dish.get(4); + + // Check intermediate value + const check = new Uint8Array(dish.value); + assert.deepEqual(check, new Uint8Array([1, 2, 3])); + + // Check converts back OK + dish.get(0); + assert.deepEqual(dish.value, [1, 2, 3]); + }), + + it("Dish translation: ArrayBuffer and HTML", () => { + const html = ` + + + + + + Click here + + +`.replace(/\n|\s{4}/g, ""); //remove newlines, tabs + + const dish = new Dish(html, Dish.HTML); + dish.get(4); + + dish.get(3); + assert.strictEqual(dish.value, "Click here"); + }), + + it("Dish translation: ArrayBuffer and BigNumber", () => { + const number = BigNumber(4001); + const dish = new Dish(number, Dish.BIG_NUMBER); + + dish.get(Dish.ARRAY_BUFFER); + assert.deepStrictEqual(dish.value, new Uint8Array([0x34, 0x30, 0x30, 0x31]).buffer); + assert.strictEqual(dish.value.byteLength, 4); + + // Check the data in ArrayBuffer represents 4001 as a string. + const view = new DataView(dish.value, 0); + assert.strictEqual(String.fromCharCode(view.getUint8(0), view.getUint8(1), view.getUint8(2), view.getUint8(3)), "4001"); + + dish.get(5); + assert.deepStrictEqual(dish.value, number); + }), + + it("Dish translation: ArrayBuffer and JSON", () => { + const jsonString = "{\"a\": 123455, \"b\": { \"aa\": [1,2,3]}}"; + const dish = new Dish(JSON.parse(jsonString), Dish.JSON); + + dish.get(Dish.ARRAY_BUFFER); + dish.get(Dish.JSON); + + assert.deepStrictEqual(dish.value, JSON.parse(jsonString)); + }), + + it("Dish translation: ArrayBuffer and File", () => { + const file = new File("abcd", "unknown"); + const dish = new Dish(file, Dish.FILE); + + dish.get(Dish.ARRAY_BUFFER); + assert.deepStrictEqual(dish.value, new Uint8Array([0x61, 0x62, 0x63, 0x64]).buffer); + assert.strictEqual(dish.value.byteLength, 4); + + // Check the data in ArrayBuffer represents "abcd" + const view = new DataView(dish.value, 0); + assert.strictEqual(String.fromCharCode(view.getUint8(0), view.getUint8(1), view.getUint8(2), view.getUint8(3)), "abcd"); + + dish.get(Dish.FILE); + + assert.deepStrictEqual(dish.value.data, file.data); + assert.strictEqual(dish.value.name, file.name); + assert.strictEqual(dish.value.type, file.type); + // Do not test lastModified + }), + + it("Dish translation: ArrayBuffer and ListFile", () => { + const file1 = new File("abcde", "unknown"); + const file2 = new File("fghijk", "unknown"); + + const dish = new Dish([file1, file2], Dish.LIST_FILE); + + dish.get(Dish.ARRAY_BUFFER); + assert.deepStrictEqual(dish.value, new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b]).buffer); + assert.strictEqual(dish.value.byteLength, 11); + + dish.get(Dish.LIST_FILE); + const dataArray = new Uint8Array(dish.value[0].data); + // cant store chars in a Uint8Array, so make it a normal one. + const actual = Array.prototype.slice.call(dataArray).map(c => String.fromCharCode(c)).join(""); + assert.strictEqual(actual, "abcdefghijk"); + }), +]); diff --git a/tests/node/tests/nodeApi.mjs b/tests/node/tests/nodeApi.mjs new file mode 100644 index 00000000..e775f3a0 --- /dev/null +++ b/tests/node/tests/nodeApi.mjs @@ -0,0 +1,375 @@ +/* eslint no-console: 0 */ + +/** + * nodeApi.js + * + * Test node api utilities + * + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import assert from "assert"; +import it from "../assertionHandler.mjs"; +import chef from "../../../src/node/index.mjs"; +import OperationError from "../../../src/core/errors/OperationError.mjs"; +import NodeDish from "../../../src/node/NodeDish.mjs"; + +import { toBase32} from "../../../src/node/index.mjs"; +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addApiTests([ + it("should have some operations", () => { + assert(chef); + assert(chef.toBase32); + assert(chef.setUnion); + assert(!chef.randomFunction); + }), + + it("should export other functions at top level", () => { + assert(toBase32); + }), + + it("should be synchronous", () => { + try { + const result = chef.toBase32("input"); + assert.notEqual("something", result); + } catch (e) { + // shouldnt reach here + assert(false); + } + + try { + const fail = chef.setUnion("1"); + // shouldnt get here + assert(!fail || false); + } catch (e) { + assert(true); + } + }), + + it("should not catch Errors", () => { + try { + chef.setUnion("1"); + assert(false); + } catch (e) { + assert(e instanceof OperationError); + } + }), + + it("should accept arguments in object format for operations", () => { + const result = chef.setUnion("1 2 3 4:3 4 5 6", { + itemDelimiter: " ", + sampleDelimiter: ":" + }); + + assert.equal(result.value, "1 2 3 4 5 6"); + }), + + it("should accept just some of the optional arguments being overriden", () => { + const result = chef.setIntersection("1 2 3 4 5\\n\\n3 4 5", { + itemDelimiter: " ", + }); + + assert.equal(result.value, "3 4 5"); + }), + + it("should accept no override arguments and just use the default values", () => { + const result = chef.powerSet("1,2,3"); + assert.equal(result.value, "\n3\n2\n1\n2,3\n1,3\n1,2\n1,2,3\n"); + }), + + it("should return an object with a .to method", () => { + const result = chef.toBase32("input"); + assert(result.to); + assert.equal(result.to("string"), "NFXHA5LU"); + }), + + it("should return an object with a .get method", () => { + const result = chef.toBase32("input"); + assert(result.get); + assert.equal(result.get("string"), "NFXHA5LU"); + }), + + it("should return a NodeDish", async () => { + const result = chef.toBase32("input"); + assert(result instanceof NodeDish); + }), + + it("should coerce to a string as you expect", () => { + const result = chef.fromBase32(chef.toBase32("something")); + assert.equal(String(result), "something"); + // This kind of coercion uses toValue + assert.equal(""+result, "NaN"); + }), + + it("should coerce to a number as you expect", () => { + const result = chef.fromBase32(chef.toBase32("32")); + assert.equal(3 + result, 35); + }), + + it("chef.help: should exist", () => { + assert(chef.help); + }), + + it("chef.help: should describe a operation", () => { + const result = chef.help("tripleDESDecrypt"); + assert.strictEqual(result[0].name, "Triple DES Decrypt"); + assert.strictEqual(result[0].module, "Ciphers"); + assert.strictEqual(result[0].inputType, "string"); + assert.strictEqual(result[0].outputType, "string"); + assert.strictEqual(result[0].description, "Triple DES applies DES three times to each block to increase key size.

    Key: Triple DES uses a key length of 24 bytes (192 bits).
    DES uses a key length of 8 bytes (64 bits).

    IV: The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.

    Padding: In CBC and ECB mode, PKCS#7 padding will be used."); + assert.strictEqual(result[0].args.length, 5); + }), + + it("chef.help: null for invalid operation", () => { + const result = chef.help("some invalid function name"); + assert.strictEqual(result, null); + }), + + it("chef.help: takes a wrapped operation as input", () => { + const result = chef.help(chef.toBase32); + assert.strictEqual(result[0].name, "To Base32"); + assert.strictEqual(result[0].module, "Default"); + }), + + it("chef.help: returns multiple results", () => { + const result = chef.help("base 64"); + assert.strictEqual(result.length, 10); + }), + + it("chef.help: looks in description for matches too", () => { + // string only in one operation's description. + const result = chef.help("Converts a unit of data to another format."); + assert.strictEqual(result.length, 1); + assert.strictEqual(result[0].name, "Convert data units"); + }), + + it("chef.help: lists name matches before desc matches", () => { + const result = chef.help("Checksum"); + assert.ok(result[0].name.includes("Checksum")); + assert.ok(result[1].name.includes("Checksum")); + assert.strictEqual(result[result.length - 1].name.includes("Checksum"), false); + assert.ok(result[result.length - 1].description.includes("checksum")); + }), + + it("chef.help: exact name match only returns one result", () => { + const result = chef.help("MD5"); + assert.strictEqual(result.length, 1); + assert.strictEqual(result[0].name, "MD5"); + }), + + it("chef.help: exact match ignores whitespace", () => { + const result = chef.help("tobase64"); + assert.strictEqual(result.length, 1); + assert.strictEqual(result[0].name, "To Base64"); + }), + + it("chef.bake: should exist", () => { + assert(chef.bake); + }), + + it("chef.bake: should return NodeDish", () => { + const result = chef.bake("input", "to base 64"); + assert(result instanceof NodeDish); + }), + + it("chef.bake: should take an input and an op name and perform it", () => { + const result = chef.bake("some input", "to base 32"); + assert.strictEqual(result.toString(), "ONXW2ZJANFXHA5LU"); + }), + + it("chef.bake: should complain if recipe isnt a valid object", () => { + try { + chef.bake("some input", 3264); + } catch (e) { + assert.strictEqual(e.name, "TypeError"); + assert.strictEqual(e.message, "Recipe can only contain function names or functions"); + } + }), + + it("chef.bake: Should complain if string op is invalid", () => { + try { + chef.bake("some input", "not a valid operation"); + assert.fail("Shouldn't be hit"); + } catch (e) { + assert.strictEqual(e.name, "TypeError"); + assert.strictEqual(e.message, "Couldn't find an operation with name 'not a valid operation'."); + } + }), + + it("chef.bake: Should take an input and an operation and perform it", () => { + const result = chef.bake("https://google.com/search?q=help", chef.parseURI); + assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = help\n"); + }), + + it("chef.bake: Should complain if an invalid operation is inputted", () => { + try { + chef.bake("https://google.com/search?q=help", () => {}); + assert.fail("Shouldn't be hit"); + } catch (e) { + assert.strictEqual(e.name, "TypeError"); + assert.strictEqual(e.message, "Inputted function not a Chef operation."); + } + }), + + it("chef.bake: accepts an array of operation names and performs them all in order", () => { + const result = chef.bake("https://google.com/search?q=that's a complicated question", ["URL encode", "URL decode", "Parse URI"]); + assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n"); + }), + + it("chef.bake: forgiving with operation names", () =>{ + const result = chef.bake("https://google.com/search?q=that's a complicated question", ["urlencode", "url decode", "parseURI"]); + assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n"); + }), + + it("chef.bake: forgiving with operation names", () =>{ + const result = chef.bake("hello", ["to base 64"]); + assert.strictEqual(result.toString(), "aGVsbG8="); + }), + + it("chef.bake: if recipe is empty array, return input as dish", () => { + const result = chef.bake("some input", []); + assert.strictEqual(result.toString(), "some input"); + assert(result instanceof NodeDish, "Result is not instance of NodeDish"); + }), + + it("chef.bake: accepts an array of operations as recipe", () => { + const result = chef.bake("https://google.com/search?q=that's a complicated question", [chef.URLEncode, chef.URLDecode, chef.parseURI]); + assert.strictEqual(result.toString(), "Protocol:\thttps:\nHostname:\tgoogle.com\nPath name:\t/search\nArguments:\n\tq = that's a complicated question\n"); + }), + + it("should complain if an invalid operation is inputted as part of array", () => { + try { + chef.bake("something", [() => {}]); + } catch (e) { + assert.strictEqual(e.name, "TypeError"); + assert.strictEqual(e.message, "Inputted function not a Chef operation."); + } + }), + + it("chef.bake: should take single JSON object describing op and args OBJ", () => { + const result = chef.bake("some input", { + op: chef.toHex, + args: { + Delimiter: "Colon" + } + }); + assert.strictEqual(result.toString(), "73:6f:6d:65:20:69:6e:70:75:74"); + }), + + it("chef.bake: should take single JSON object describing op and args ARRAY", () => { + const result = chef.bake("some input", { + op: chef.toHex, + args: ["Colon"] + }); + assert.strictEqual(result.toString(), "73:6f:6d:65:20:69:6e:70:75:74"); + }), + + it("chef.bake: should error if op in JSON is not chef op", () => { + try { + chef.bake("some input", { + op: () => {}, + args: ["Colon"], + }); + } catch (e) { + assert.strictEqual(e.name, "TypeError"); + assert.strictEqual(e.message, "Inputted function not a Chef operation."); + } + }), + + it("chef.bake: should take multiple ops in JSON object form, some ops by string", () => { + const result = chef.bake("some input", [ + { + op: chef.toHex, + args: ["Colon"] + }, + { + op: "to octal", + args: { + delimiter: "Semi-colon", + } + } + ]); + assert.strictEqual(result.toString(), "67;63;72;66;146;72;66;144;72;66;65;72;62;60;72;66;71;72;66;145;72;67;60;72;67;65;72;67;64"); + }), + + it("chef.bake: should handle op with multiple args", () => { + const result = chef.bake("some input", { + op: "to morse code", + args: { + formatOptions: "Dash/Dot", + wordDelimiter: "Comma", + letterDelimiter: "Backslash", + } + }); + assert.strictEqual(result.toString(), "DotDotDot\\DashDashDash\\DashDash\\Dot,DotDot\\DashDot\\DotDashDashDot\\DotDotDash\\Dash"); + }), + + it("chef.bake: should take compact JSON format from Chef Website as recipe", () => { + const result = chef.bake("some input", [{"op": "To Morse Code", "args": ["Dash/Dot", "Backslash", "Comma"]}, {"op": "Hex to PEM", "args": ["SOMETHING"]}, {"op": "To Snake case", "args": [false]}]); + assert.strictEqual(result.toString(), "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something"); + }), + + it("chef.bake: should accept Clean JSON format from Chef website as recipe", () => { + const result = chef.bake("some input", [ + { "op": "To Morse Code", + "args": ["Dash/Dot", "Backslash", "Comma"] }, + { "op": "Hex to PEM", + "args": ["SOMETHING"] }, + { "op": "To Snake case", + "args": [false] } + ]); + assert.strictEqual(result.toString(), "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something"); + }), + + it("Excluded operations: throw a sensible error when you try and call one", () => { + try { + chef.fork(); + } catch (e) { + assert.strictEqual(e.type, "ExcludedOperationError"); + assert.strictEqual(e.message, "Sorry, the Fork operation is not available in the Node.js version of CyberChef."); + } + }), + + it("Excluded operations: throw a sensible error when you try and call one", () => { + try { + chef.renderImage(); + } catch (e) { + assert.strictEqual(e.type, "ExcludedOperationError"); + assert.strictEqual(e.message, "Sorry, the RenderImage operation is not available in the Node.js version of CyberChef."); + } + }), + + it("Operation arguments: should be accessible from operation object if op has array arg", () => { + assert.ok(chef.toCharcode.args); + assert.deepEqual(chef.unzip.args, { + password: { + type: "binaryString", + value: "", + }, + verifyResult: { + type: "boolean", + value: false, + } + }); + }), + + it("Operation arguments: should have key for each argument in operation", () => { + assert.ok(chef.convertDistance.args.inputUnits); + assert.ok(chef.convertDistance.args.outputUnits); + + assert.strictEqual(chef.bitShiftRight.args.amount.type, "number"); + assert.strictEqual(chef.bitShiftRight.args.amount.value, 1); + assert.strictEqual(chef.bitShiftRight.args.type.type, "option"); + assert.ok(Array.isArray(chef.bitShiftRight.args.type.options)); + + }), + + it("Operation arguments: should list all options excluding subheadings", () => { + // First element (subheading) removed + assert.equal(chef.convertDistance.args.inputUnits.options[0], "Nanometres (nm)"); + assert.equal(chef.defangURL.args.process.options[1], "Only full URLs"); + }), +]); diff --git a/tests/node/tests/operations.mjs b/tests/node/tests/operations.mjs new file mode 100644 index 00000000..ecbfb570 --- /dev/null +++ b/tests/node/tests/operations.mjs @@ -0,0 +1,1033 @@ +/* eslint no-console: 0 */ + +/** + * nodeApi.js + * + * Test node api operations + * + * Aim of these tests is to ensure each arg type is + * handled correctly by the wrapper. + * + * Generally just checking operations that use external dependencies to ensure + * it behaves as expected in Node. + * + * @author d98762625 [d98762625@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import assert from "assert"; +import it from "../assertionHandler.mjs"; +import fs from "fs"; + +import { + addLineNumbers, + adler32Checksum, + AESDecrypt, + affineCipherDecode, + affineCipherEncode, + bifidCipherEncode, + bitShiftRight, + cartesianProduct, + CSSMinify, + toBase64, + toHex, +} from "../../../src/node/index"; +import chef from "../../../src/node/index.mjs"; +import TestRegister from "../../lib/TestRegister.mjs"; +import File from "../../../src/node/File.mjs"; + +global.File = File; + +TestRegister.addApiTests([ + + it("ADD: toggleString argument", () => { + const result = chef.ADD("sample input", { + key: { + string: "some key", + option: "Hex" + } + }); + assert.equal(result.toString(), "aO[^ZS\u000eW\\^cb"); + }), + + + it("ADD: default option toggleString argument", () => { + const result = chef.ADD(3, { + key: "4", + }); + assert.strictEqual(result.toString(), "7"); + }), + + it("addLineNumbers: No arguments", () => { + const result = addLineNumbers("sample input"); + assert.equal(result.toString(), "1 sample input"); + }), + + it("adler32Checksum: No args", () => { + const result = adler32Checksum("sample input"); + assert.equal(result.toString(), "1f2304d3"); + }), + + it("AES decrypt: toggleString and option", () => { + const result = AESDecrypt("812c34ae6af353244a63c6ce23b7c34286b60be28ea4645523d4494700e7", { + key: { + string: "some longer key1", + option: "utf8", + }, + iv: { + string: "some iv", + option: "utf8", + }, + mode: "OFB", + }); + assert.equal(result.toString(), "a slightly longer sampleinput?"); + }), + + it("AffineCipherDecode: number input", () => { + const result = affineCipherDecode("some input", { + a: 7, + b: 4 + }); + assert.strictEqual(result.toString(), "cuqa ifjgr"); + }), + + it("affineCipherEncode: number input", () => { + const result = affineCipherEncode("some input", { + a: 11, + b: 6 + }); + assert.strictEqual(result.toString(), "weiy qtpsh"); + }), + + it("analyzeHash", () => { + const result = chef.analyseHash(chef.MD5("some input")); + const expected = `Hash length: 32 +Byte length: 16 +Bit length: 128 + +Based on the length, this hash could have been generated by one of the following hashing functions: +MD5 +MD4 +MD2 +HAVAL-128 +RIPEMD-128 +Snefru +Tiger-128`; + assert.strictEqual(result.toString(), expected); + }), + + it("AND", () => { + const result = chef.AND("Scot-free", { + key: { + string: "Raining Cats and Dogs", + option: "Hex", + } + }); + assert.strictEqual(result.toString(), "\u0000\"M$(D E"); + }), + + it("atBash Cipher", () => { + const result = chef.atbashCipher("Happy as a Clam"); + assert.strictEqual(result.toString(), "Szkkb zh z Xozn"); + }), + + it("Bcrypt", async () => { + const result = await chef.bcrypt("Put a Sock In It"); + assert.strictEqual(result.toString(), "$2a$10$ODeP1.6fMsb.ENk2ngPUCO7qTGVPyHA9TqDVcyupyed8FjsiF65L6"); + }), + + it("bcryptCompare", async() => { + const result = await chef.bcryptCompare("Put a Sock In It", { + hash: "$2a$10$2rT4a3XnIecBsd1H33dMTuyYE1HJ1n9F.V2rjQtAH73rh1qvOf/ae", + }); + assert.strictEqual(result.toString(), "Match: Put a Sock In It"); + }), + + it("Bcrypt Parse", async () => { + const result = await chef.bcryptParse("$2a$10$ODeP1.6fMsb.ENk2ngPUCO7qTGVPyHA9TqDVcyupyed8FjsiF65L6"); + const expected = `Rounds: 10 +Salt: $2a$10$ODeP1.6fMsb.ENk2ngPUCO +Password hash: 7qTGVPyHA9TqDVcyupyed8FjsiF65L6 +Full hash: $2a$10$ODeP1.6fMsb.ENk2ngPUCO7qTGVPyHA9TqDVcyupyed8FjsiF65L6`; + assert.strictEqual(result.toString(), expected); + }), + + it("bifid cipher decode", () => { + const result = chef.bifidCipherDecode("Vhef Qnte Ke Xfhz Mxon Bmgf", { + keyword: "Alpha", + }); + assert.strictEqual(result.toString(), "What Goes Up Must Come Down"); + }), + + it("bifid cipher encode: string option", () => { + const result = bifidCipherEncode("some input", { + keyword: "mykeyword", + }); + assert.strictEqual(result.toString(), "nmhs zmsdo"); + }), + + it("bit shift left", () => { + const result = chef.bitShiftLeft("Keep Your Eyes Peeled"); + assert.strictEqual(result.toString(), "–ÊÊà@²Þêä@ŠòÊæ@ ÊÊØÊÈ"); + }), + + it("bitShiftRight: number and option", () => { + const result = bitShiftRight("some bits to shift", { + type: "Arithmetic shift", + amount: 1, + }); + assert.strictEqual(result.toString(), "9762\u001014:9\u0010:7\u00109443:"); + }), + + it("Blowfish encrypt", () => { + const result = chef.blowfishEncrypt("Fool's Gold", { + key: { + string: "One", + option: "hex", + }, + iv: { + string: "Two", + option: "utf8" + } + }); + assert.strictEqual(result.toString(), "8999b513bf2ff064b2977dea7e05f1b5"); + }), + + it("Blowfish decrypt", () => { + const result = chef.blowfishDecrypt("8999b513bf2ff064b2977dea7e05f1b5", { + key: { + string: "One", + option: "hex", + }, + iv: { + string: "Two", + option: "utf8", + } + }); + assert.strictEqual(result.toString(), "Fool's Gold"); + }), + + it("BSON Serialise / Deserialise", () => { + const result = chef.BSONDeserialise(chef.BSONSerialise("{\"phrase\": \"Mouth-watering\"}")); + assert.strictEqual(result.toString(), `{ + "phrase": "Mouth-watering" +}`); + }), + + it("Bzip2 Decompress", async () => { + const result = await chef.bzip2Decompress(chef.fromBase64("QlpoOTFBWSZTWUdQlt0AAAIVgEAAAQAmJAwAIAAxBkxA0A2pTL6U2CozxdyRThQkEdQlt0A=")); + assert.strictEqual(result.toString(), "Fit as a Fiddle"); + }), + + it("cartesianProduct: binary string", () => { + const result = cartesianProduct("1:2\\n\\n3:4", { + itemDelimiter: ":", + }); + assert.strictEqual(result.toString(), "(1,3):(1,4):(2,3):(2,4)"); + }), + + it("Change IP format", () => { + const result = chef.changeIPFormat("172.20.23.54", { + inputFormat: "Dotted Decimal", + outputFormat: "Hex", + }); + assert.strictEqual(result.toString(), "ac141736"); + }), + + it("Chi square", () => { + const result = chef.chiSquare("Burst Your Bubble"); + assert.strictEqual(result.toString(), "433.55399816176475"); + }), + + it("Compare CTPH Hashes", () => { + const result = chef.compareCTPHHashes("1234\n3456"); + assert.strictEqual(result.toString(), "0"); + }), + + it("Compare SSDEEPHashes", () => { + const result = chef.compareCTPHHashes("1234\n3456"); + assert.strictEqual(result.toString(), "0"); + }), + + it("Convert area", () => { + const result = chef.convertArea("12345", { + inputUnits: "Square metre (sq m)", + outputUnits: "Isle of Wight" + }); + assert.strictEqual(result.toString(), "0.00003248684210526316"); + }), + + it("Convert data units", () => { + const result = chef.convertDataUnits("12345", { + inputUnits: "Bits (b)", + outputUnits: "Kilobytes (KB)", + }); + assert.strictEqual(result.toString(), "1.543125"); + }), + + it("Convert distance", () => { + const result = chef.convertDistance("1234567", { + inputUnits: "Nanometres (nm)", + outputUnits: "Furlongs (fur)", + }); + assert.strictEqual(result.toString(), "0.00000613699494949495"); + }), + + it("Convert mass", () => { + const result = chef.convertMass("123", { + inputUnits: "Earth mass (M⊕)", + outputUnits: "Great Pyramid of Giza (6,000,000 tonnes)", + }); + assert.strictEqual(result.toString(), "122429895000000000"); + }), + + it("Convert speed", () => { + const result = chef.convertSpeed("123", { + inputUnits: "Lunar escape velocity", + outputUnits: "Jet airliner cruising speed", + }); + assert.strictEqual(result.toString(), "1168.5"); + }), + + it("Count occurrences", () => { + const result = chef.countOccurrences("Talk the Talk", { + searchString: { + string: "Tal", + option: "Simple string", + } + }); + assert.strictEqual(result.toString(), "2"); + }), + + it("CRC16 Checksum", () => { + const result = chef.CRC16Checksum("Rain on Your Parade"); + assert.strictEqual(result.toString(), "db1c"); + }), + + it("CRC32 Checksum", () => { + const result = chef.CRC32Checksum("Rain on Your Parade"); + assert.strictEqual(result.toString(), "e902f76c"); + }), + + it("CSS Beautify", () => { + const result = chef.CSSBeautify("header {color:black;padding:3rem;}"); + const expected = `header { +\\tcolor:black; +\\tpadding:3rem; +} +`; + assert.strictEqual(result.toString(), expected); + }), + + it("CSS minify: boolean", () => { + const input = `header { +// comment +width: 100%; +color: white; +}`; + const result = CSSMinify(input, { + preserveComments: true, + }); + assert.strictEqual(result.toString(), "header {// comment width: 100%;color: white;}"); + }), + + it("CSS Selector", () => { + const result = chef.CSSSelector("

    Hello

    ", { + cssSelector: "h1", + }); + assert.strictEqual(result.toString(), "

    Hello

    "); + }), + + it("CTPH", () => { + const result = chef.CTPH("If You Can't Stand the Heat, Get Out of the Kitchen"); + assert.strictEqual(result.toString(), "A:+EgFgBKAA0V0UFfClEs6:+Qk0gUFse"); + }), + + it("Decode NetBIOS Name", () => { + assert.strictEqual(chef.decodeNetBIOSName("EBGMGMCAEHHCGFGFGLCAFEGPCAENGFCA").toString(), "All Greek To Me"); + }), + + it("Decode text", () => { + const encoded = chef.encodeText("Ugly Duckling", { + encoding: "UTF16LE (1200)", + }); + const result = chef.decodeText(encoded, { + encoding: "UTF16LE (1200)", + }); + assert.strictEqual(result.toString(), "Ugly Duckling"); + }), + + it("Derive EVP Key", () => { + const result = chef.deriveEVPKey("", { + passphrase: { + string: "46 6c 65 61 20 4d 61 72 6b 65 74", + option: "Hex", + }, + salt: { + string: "Market", + option: "Hex", + }, + }); + assert.strictEqual(result.toString(), "7c21a9f5063a4d62fb1050068245c181"); + }), + + it("Derive PBKDF2 Key", () => { + const result = chef.derivePBKDF2Key("", { + passphrase: { + string: "Jack of All Trades Master of None", + option: "utf8", + }, + keySize: 256, + iterations: 2, + hashingFunction: "md5", + salt: { + string: "fruit", + option: "utf8" + } + }); + assert.strictEqual(result.toString(), "728a885b209e8b19cbd7430ca32608ff09190f7ccb7ded204e1d4c50f87c47bf"); + }), + + it("DES Decrypt", () => { + const result = chef.DESDecrypt("713081c66db781c323965ba8f166fd8c230c3bb48504a913", { + key: { + string: "onetwoth", + option: "utf8", + }, + iv: { + string: "threetwo", + option: "Hex", + }, + mode: "ECB", + }); + assert.strictEqual(result.toString(), "Put a Sock In It"); + }), + + it("DES Encrypt", () => { + const result = chef.DESEncrypt("Put a Sock In It", { + key: { + string: "onetwoth", + option: "utf8", + }, + iv: { + string: "threetwo", + option: "Hex", + }, + mode: "ECB", + }); + assert.strictEqual(result.toString(), "713081c66db781c323965ba8f166fd8c230c3bb48504a913"); + }), + + it("Diff", () => { + const result = chef.diff("one two\\n\\none two three"); + assert.strictEqual(result.toString(), "one two three"); + }), + + it("Disassemble x86", () => { + const result = chef.disassembleX86(chef.toBase64("one two three")); + const expected = `0000000000000000 0000 ADD BYTE PTR [RAX],AL\r +0000000000000002 0B250000000B OR ESP,DWORD PTR [0000000-F4FFFFF8]\r +`; + assert.strictEqual(result.toString(), expected); + }), + + it("Divide", () => { + assert.strictEqual(chef.divide("4\n7").toString(), "0.57142857142857142857"); + }), + + it("Drop bytes", () => { + assert.strictEqual(chef.dropBytes("There's No I in Team").toString(), "'s No I in Team"); + }), + + it("Entropy", () => { + const result = chef.entropy("Ride Him, Cowboy!"); + assert.strictEqual(result.toString(), "3.734521664779752"); + }), + + it("Escape string", () => { + const result = chef.escapeString("Know the Ropes", { + escapeLevel: "Everything", + JSONCompatible: false, + ES6Compatible: true, + uppercaseHex: true, + }); + assert.strictEqual(result.toString(), "\\x4B\\x6E\\x6F\\x77\\x20\\x74\\x68\\x65\\x20\\x52\\x6F\\x70\\x65\\x73"); + }), + + it("Escape unicode characters", () => { + assert.strictEqual(chef.escapeUnicodeCharacters("σου").toString(), "\\u03C3\\u03BF\\u03C5"); + }), + + it("Expand alphabet range", () => { + assert.strictEqual( + chef.expandAlphabetRange("Fight Fire With Fire", {delimiter: "t"}).toString(), + "Ftitgthttt tFtitrtet tWtitttht tFtitrte"); + }), + + it("Extract dates", () => { + assert.strictEqual(chef.extractDates("Don't Look a Gift Horse In The Mouth 01/02/1992").toString(), "01/02/1992\n"); + }), + + it("Filter", () => { + const result = chef.filter( + `I Smell a Rat +Every Cloud Has a Silver Lining +Top Drawer`, { + regex: "Every", + }); + const expected = "Every Cloud Has a Silver Lining"; + assert.strictEqual(result.toString(), expected); + }), + + it("Find / Replace", () => { + assert.strictEqual( + chef.findReplace( + "Curiosity Killed The Cat", + { + find: { + string: "l", + option: "Regex", + }, + replace: "s", + }).toString(), + "Curiosity Kissed The Cat"); + }), + + it("Fletcher8 Checksum", () => { + assert.strictEqual(chef.fletcher8Checksum("Keep Your Eyes Peeled").toString(), "48"); + }), + + it("Format MAC addresses", () => { + const result = chef.formatMACAddresses("00-01-02-03-04-05"); + const expected = `000102030405 +000102030405 +00-01-02-03-04-05 +00-01-02-03-04-05 +00:01:02:03:04:05 +00:01:02:03:04:05 +`; + assert.strictEqual(result.toString(), expected); + }), + + it("Frequency distribution", () => { + const result = chef.frequencyDistribution("Don't Count Your Chickens Before They Hatch"); + const expected = "{\"dataLength\":43,\"percentages\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13.953488372093023,0,0,0,0,0,0,2.3255813953488373,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2.3255813953488373,4.651162790697675,2.3255813953488373,0,0,0,2.3255813953488373,0,0,0,0,0,0,0,0,0,0,0,2.3255813953488373,0,0,0,0,2.3255813953488373,0,0,0,0,0,0,0,2.3255813953488373,0,4.651162790697675,0,9.30232558139535,2.3255813953488373,0,6.976744186046512,2.3255813953488373,0,2.3255813953488373,0,0,6.976744186046512,9.30232558139535,0,0,4.651162790697675,2.3255813953488373,6.976744186046512,4.651162790697675,0,0,0,2.3255813953488373,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"distribution\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,2,0,4,1,0,3,1,0,1,0,0,3,4,0,0,2,1,3,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"bytesRepresented\":22}"; + // Whacky formatting, but the data is all there + assert.strictEqual(result.toString().replace(/\r?\n|\r|\s/g, ""), expected); + }), + + it("From base", () => { + assert.strictEqual(chef.fromBase("11", {radix: 13}).toString(), "14"); + }), + + it("From BCD", () => { + assert.strictEqual(chef.fromBCD("1143", { inputFormat: "Raw", scheme: "7 4 2 1"}).toString(), "31313433"); + }), + + it("From binary", () => { + assert.strictEqual(chef.fromBinary("010101011100101101011010").toString(), "UËZ"); + }), + + it("From Charcode", () => { + assert.strictEqual(chef.fromCharcode("4c 6f 6e 67 20 49 6e 20 54 68 65 20 54 6f 6f 74 68 0a").toString(), "Long In The Tooth\n"); + }), + + it("From decimal", () => { + assert.strictEqual(chef.fromDecimal("72 101 108 108 111").toString(), "Hello"); + }), + + it("From hex", () => { + assert.strictEqual(chef.fromHex("52 69 6e 67 20 41 6e 79 20 42 65 6c 6c 73 3f").toString(), "Ring Any Bells?"); + }), + + it("From hex content", () => { + assert.strictEqual(chef.fromHexContent("foo|3d|bar").toString(), "foo=bar"); + }), + + it("To and From hex dump", () => { + assert.strictEqual(chef.fromHexdump(chef.toHexdump("Elephant in the Room")).toString(), "Elephant in the Room"); + }), + + it("From HTML entity", () => { + assert.strictEqual(chef.fromHTMLEntity("&").toString(), "&"); + }), + + it("To and From morse code", () => { + assert.strictEqual(chef.fromMorseCode(chef.toMorseCode("Put a Sock In It")).toString(), "PUT A SOCK IN IT"); + }), + + it("From octal", () => { + assert.strictEqual(chef.fromOctal("113 156 157 167 40 164 150 145 40 122 157 160 145 163").toString(), "Know the Ropes"); + }), + + it("To, From punycode", () => { + assert.strictEqual(chef.fromPunycode(chef.toPunycode("münchen")).toString(), "münchen"); + }), + + it("From unix timestamp", () => { + assert.strictEqual(chef.fromUNIXTimestamp("978346800").toString(), "Mon 1 January 2001 11:00:00 UTC"); + }), + + it("Generate HOTP", () => { + const result = chef.generateHOTP("Cut The Mustard", { + name: "colonel", + }); + const expected = `URI: otpauth://hotp/colonel?secret=IN2XIICUNBSSATLVON2GC4TE + +Password: 034148`; + assert.strictEqual(result.toString(), expected); + }), + + it("Generate PGP Key Pair", async () => { + const result = await chef.generatePGPKeyPair("Back To the Drawing Board", { + keyType: "ECC-256", + }); + const expected = `-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: Keybase OpenPGP v2.0.77 +Comment: https://keybase.io/crypto + +xYgEW3KciQEBAK96Lx9G0WZiw1yhC35IogdumoxEJXsLdAVIjmskXeAfABEBAAEA +AP4wK+OZu3AqojwtRoyIK1pHKU93OAuam1iaLCOGCwCckQCA5PjU0aLNZqy/eKyX +T3rpKQCAxDDT5hHGAUfFPUu73KWABwB/WKpeUp7KwurMSbYVhgr1TijszQDCVAQT +AQoAHgUCW3KciQIbLwMLCQcDFQoIAh4BAheAAxYCAQIZAQAKCRD0VeyUMgmpz3OE +AP9qsnhhoK85Tnu6VKwKm1iMiJAssDQnFztDaMmmVdrN/MeIBFtynIkBAQDDhjIw +fxOprqVMYLk6aC45JyPAA2POzu0Zb/rx0tKeBwARAQABAAD/XAr66oiP9ZORHiT0 +XZH4m7vwZt7AHuq4pYtVlMQXk60AgPw2Mno/wStvE/SBa9R7AtsAgMZ2BkJjvNPZ +9YA6cl4lW0UAgI1+kJVLZ5VR9fPENfJR80EtncKDBBgBCgAPBQJbcpyJBQkPCZwA +AhsuAEgJEPRV7JQyCanPPSAEGQEKAAYFAltynIkACgkQrewgWMQZ/b2blwD/dbwh +/3F9xv+YGAwq8i1mzzswg4qBct6LoSIjGglULT9RIQD/cYd31YfKrEnbSBWD5PLi +zcSsxtBGKphwXiPAlQJ1Q5DHiARbcpyJAQEAs8V418lf1T74PpAwdBTiViEUX9jB +e+ZrAEVaq5nu1C8AEQEAAQAA/iPWhS23hnRTllealR4/H5OofZRwxvIQrxAJp6z1 +ICRxAIDayRpCAbK5EC3DzRU2z4VpAIDSWYSs9inI1VTfamJPMWHXAIC3aaukzCP4 +GEGeFobX/thnKhnCgwQYAQoADwUCW3KciQUJA8JnAAIbLgBICRD0VeyUMgmpzz0g +BBkBCgAGBQJbcpyJAAoJEB4jzL1hmQIXamUA/0c1M6BSqVtxNowPcOAXKYIxMca1 +VFcRWolHnZqdZQ7k/J8A/3HvNLRS3p1HvjQEfXl/qKoRRn843Py09ptDHh+xpGKh +=d+Vp +-----END PGP PRIVATE KEY BLOCK----- + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: Keybase OpenPGP v2.0.77 +Comment: https://keybase.io/crypto + +xi0EW3KciQEBAK96Lx9G0WZiw1yhC35IogdumoxEJXsLdAVIjmskXeAfABEBAAHN +AMJUBBMBCgAeBQJbcpyJAhsvAwsJBwMVCggCHgECF4ADFgIBAhkBAAoJEPRV7JQy +CanPc4QA/2qyeGGgrzlOe7pUrAqbWIyIkCywNCcXO0NoyaZV2s38zi0EW3KciQEB +AMOGMjB/E6mupUxguTpoLjknI8ADY87O7Rlv+vHS0p4HABEBAAHCgwQYAQoADwUC +W3KciQUJDwmcAAIbLgBICRD0VeyUMgmpzz0gBBkBCgAGBQJbcpyJAAoJEK3sIFjE +Gf29m5cA/3W8If9xfcb/mBgMKvItZs87MIOKgXLei6EiIxoJVC0/USEA/3GHd9WH +yqxJ20gVg+Ty4s3ErMbQRiqYcF4jwJUCdUOQzi0EW3KciQEBALPFeNfJX9U++D6Q +MHQU4lYhFF/YwXvmawBFWquZ7tQvABEBAAHCgwQYAQoADwUCW3KciQUJA8JnAAIb +LgBICRD0VeyUMgmpzz0gBBkBCgAGBQJbcpyJAAoJEB4jzL1hmQIXamUA/0c1M6BS +qVtxNowPcOAXKYIxMca1VFcRWolHnZqdZQ7k/J8A/3HvNLRS3p1HvjQEfXl/qKoR +Rn843Py09ptDHh+xpGKh +=ySwG +-----END PGP PUBLIC KEY BLOCK-----`; + assert.strictEqual(result.toString(), expected); + }), + + it("Generate UUID", () => { + const result = chef.generateUUID(); + assert.ok(result.toString()); + assert.strictEqual(result.toString().length, 36); + }), + + it("Gzip, Gunzip", () => { + assert.strictEqual(chef.gunzip(chef.gzip("Down To The Wire")).toString(), "Down To The Wire"); + }), + + it("Hex to Object Identifier", () => { + assert.strictEqual( + chef.hexToObjectIdentifier(chef.toHex("You Can't Teach an Old Dog New Tricks")).toString(), + "2.9.111.117.32.67.97.110.39.116.32.84.101.97.99.104.32.97.110.32.79.108.100.32.68.111.103.32.78.101.119.32.84.114.105.99.107.115"); + }), + + it("Hex to PEM", () => { + const result = chef.hexToPEM(chef.toHex("Yada Yada")); + const expected = `-----BEGIN CERTIFICATE-----\r +WWFkYSBZYWRh\r +-----END CERTIFICATE-----\r\n`; + assert.strictEqual(result.toString(), expected); + }), + + it("HMAC", () => { + assert.strictEqual(chef.HMAC("On Cloud Nine", {key: "idea"}).toString(), "e15c268b4ee755c9e52db094ed50add7"); + }), + + it("JPathExpression", () => { + assert.strictEqual(chef.JPathExpression("{\"key\" : \"value\"}", {query: "$.key"}).toString(), "\"value\""); + }), + + it("JSON Beautify", () => { + assert.strictEqual( + chef.JSONBeautify("{\"key\" : \"value\"}").toString(), + `{ + "key": "value" +}`); + }), + + it("Keccak", () => { + assert.strictEqual(chef.keccak("Flea Market").toString(), "c2a06880b19e453ee5440e8bd4c2024bedc15a6630096aa3f609acfd2b8f15f27cd293e1cc73933e81432269129ce954a6138889ce87831179d55dcff1cc7587"); + }), + + it("MD6", () => { + assert.strictEqual(chef.MD6("Head Over Heels", {key: "arty"}).toString(), "d8f7fe4931fbaa37316f76283d5f615f50ddd54afdc794b61da522556aee99ad"); + }), + + it("Parse ASN.1 Hex string", () => { + assert.strictEqual(chef.parseASN1HexString(chef.toHex("Mouth-watering")).toString(), "UNKNOWN(4d) 7574682d7761746572696e67\n"); + }), + + it("Parse DateTime", () => { + const result = chef.parseDateTime("06/07/2001 01:59:30"); + const expected = `Date: Friday 6th July 2001 +Time: 01:59:30 +Period: AM +Timezone: UTC +UTC offset: +0000 + +Daylight Saving Time: false +Leap year: false +Days in this month: 31 + +Day of year: 187 +Week number: 2001 +Quarter: 3`; + assert.strictEqual(result.toString(), expected); + }), + + it("Parse IPV6 address", () => { + const result = chef.parseIPv6Address("2001:0db8:85a3:0000:0000:8a2e:0370:7334"); + const expected = `Longhand: 2001:0db8:85a3:0000:0000:8a2e:0370:7334 +Shorthand: 2001:db8:85a3::8a2e:370:7334 + +This is a documentation IPv6 address. This range should be used whenever an example IPv6 address is given or to model networking scenarios. Corresponds to 192.0.2.0/24, 198.51.100.0/24, and 203.0.113.0/24 in IPv4. +Documentation range: 2001:db8::/32`; + assert.strictEqual(result.toString(), expected); + }), + + it("Parse URI", () => { + const result = chef.parseURI("https://www.google.co.uk/search?q=almonds"); + const expected = `Protocol: https: +Hostname: www.google.co.uk +Path name: /search +Arguments: +\tq = almonds +`; + assert.strictEqual(result.toString(), expected); + }), + + it("Parse user agent", () => { + const result = chef.parseUserAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0 "); + const expected = `Browser + Name: Mozilla + Version: 5.0 +Device + Model: unknown + Type: unknown + Vendor: unknown +Engine + Name: Gecko + Version: 47.0 +OS + Name: Windows + Version: 7 +CPU + Architecture: amd64`; + assert.strictEqual(result.toString(), expected); + }), + + it("PGP Encrypt", async () => { + const pbkey = `-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +mI0EVWOihAEEALzwFAVWTrD0KiWCH5tX6q6QsGjlRn4IP2uj/xWsJZDNbCKm+JAe +1RvIootpW1+PNNMJlIInwUgtCjtJ9gZbGBpXeqwdSn0oYuj9X86ekXOUnZsRoPCj +RwS8kpbrvRVfhWN8hYuXFcXK2J2Ld0ZpVyJzkncpFdpAgzTPMfrO1HS5ABEBAAG0 +GmdwZyBuYW1lIChjb21tZW50KSA8ZW1AaWw+iLgEEwECACIFAlVjooQCGwMGCwkI +BwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEBg8dTRwi4g5I40D/2+uUuQxa3uMrAeI +dXLaJWz3V0cl1rotfBP47apDUGbkm1HVgULJUo8Bo15Ii83ST8TUsyja3XcLutHb +IwYSWo41gEV48+NKoN6Oy3HwqBoHfH06bu0If75vdSjnZpB2dO/Ph7L9kz78gc4y +tZx4bE64MTlL2AYghZxyYpFyydjXuI0EVWOihAEEANE4UU+4iB2hMAXq93hiBzIh +AMtn/DlWbJkpdUgrjKOG6tILs28mrw9rI4PivmT7grkyNW8sa3ATsmWC1xChxGTN +T1guyh0Hhbc3Otfng2BFSWcBkPwUoNaOdrVFpP9J51IYrsQHsjbZlY45ghDBzM6t +sISfkmmFCsp0l7w/XAcvABEBAAGInwQYAQIACQUCVWOihAIbDAAKCRAYPHU0cIuI +OQ2BA/9KWqOhXZW75ac7CuJMfileZR7vRy9CkKyNG21cZtAlqftAX+m8FGdG0duU +jKHiPvjXhSfP3lmrQ7brja9LgSzkiBqQzvPW55G67nGQdUC+mqZNJNlRh+8atf9I +5nxg2i8zn6F5cLaNWz7cl27m1/mXKcH3gult1PLR4PiYLiC9aw== +=xw3e +-----END PGP PUBLIC KEY BLOCK-----`; + + const result = await chef.PGPEncrypt("A Fool and His Money are Soon Parted", { + publicKeyOfRecipient: pbkey, + }); + const expected = `-----BEGIN PGP MESSAGE----- +Version: Keybase OpenPGP v2.0.77 +Comment: https://keybase.io/crypto + +wYwDv1kIXPPNwmABA/4syW+oO+S/mfpjdp83/MZJiKh6XNQoPr/N5/1Is/QXYu9V +/v8/b+eReOpUVC6cVrJ8U5cB19y1Az3NQWHXLEC0jND2wL3cUM4sv87hlvv2PLhc +okv8OHNCitRiweo7NZHVygHGdFvY082G47e1PkyPAuVynvzdD450ta/s/KOxZdJg +ARbZIrC6WmjYNLwhbpRYawKD+3N4I5qliRpU2POKRi9UROAW9dth6egy60TTCvyO +jmPGsv1elXxVzqs58UZLD2c3vBhGkU2BV6kRKh+lj/EcVrzsFhGCz/7DKxPoDHLS +=IBYt +-----END PGP MESSAGE----- +`; + assert.strictEqual(result.toString(), expected); + }), + + it("Raw deflate", () => { + assert.strictEqual(chef.rawInflate(chef.rawDeflate("Like Father Like Son", { compressionType: "Fixed Huffman Coding"})).toString(), "Like Father Like Son"); + }), + + it("RC4", () => { + assert.strictEqual( + chef.RC4("Go Out On a Limb", {passphrase: {string: "Under Your Nose", option: "UTF8"}, inputFormat: "UTF8", outputFormat: "Hex"}).toString(), + "7d17e60d9bc94b7f4095851c729e69a2"); + }), + + it("RC4 Drop", () => { + assert.strictEqual( + chef.RC4Drop("Go Out On a Limb", {passphrase: {string: "Under Your Nose", option: "UTF8"}, inputFormat: "UTF8", outputFormat: "Hex"}).toString(), + "8fa5f2751d34476a0c857439f43816cf"); + }), + + it("Regular Expression", () => { + assert.strictEqual(chef.regularExpression("Wouldn't Harm a Fly", {regex: "\\'[a-z]"}).toString(), "Wouldn't Harm a Fly"); + }), + + it("Remove EXIF", () => { + const result = chef.removeEXIF(fs.readFileSync("tests/node/sampleData/pic.jpg")); + assert.strictEqual(result.toString().length, 4582); + }), + + it("Scan for embedded files", () => { + const result = chef.scanForEmbeddedFiles(fs.readFileSync("src/web/static/images/cook_male-32x32.png")); + const expected = "Scanning data for 'magic bytes' which may indicate embedded files."; + assert.ok(result.toString().indexOf(expected) === 0); + }), + + it("Scrypt", () => { + assert.strictEqual( + chef.scrypt("Playing For Keeps", {salt: {string: "salty", option: "Hex"}}).toString(), + "5446b6d86d88515894a163201765bceed0bc39610b1506cdc4d939ffc638bc46e051bce756e2865165d89d955a43a7eb5504502567dea8bfc9e7d49aaa894c07"); + }), + + it("SHA3", () => { + assert.strictEqual( + chef.SHA3("benign gravel").toString(), + "2b1e36e0dbe151a89887be08da3bad141908cce62327f678161bcf058627e87abe57e3c5fce6581678714e6705a207acbd5c1f37f7a812280bc2cc558f00bed9"); + }), + + it("Shake", () => { + assert.strictEqual( + chef.shake("murderous bloodshed").toString(), + "b79b3bb88099330bc6a15122f8dfaededf57a33b51c748d5a94e8122ff18d21e12f83412926b7e4a77a85ba6f36aa4841685e78296036337175e40096b5ac000"); + }), + + it("Snefru", () => { + assert.strictEqual( + chef.snefru("demeaning milestone").toString(), + "a671b48770fe073ce49e9259cc2f47d345a53712639f8ae23c5ad3fec19540a5"); + }), + + it("SQL Beautify", () => { + const result = chef.SQLBeautify(`SELECT MONTH, ID, RAIN_I, TEMP_F +FROM STATS;`); + const expected = `SELECT MONTH, + ID, + RAIN_I, + TEMP_F +FROM STATS;`; + assert.strictEqual(result.toString(), expected); + }), + + it("SSDEEP", () => { + assert.strictEqual( + chef.SSDEEP("shotgun tyranny snugly").toString(), + "3:DLIXzMQCJc:XERKc"); + }), + + it("strings", () => { + const result = chef.strings("smothering ampersand abreast", {displayTotal: true}); + const expected = `Total found: 1 + +smothering ampersand abreast +`; + assert.strictEqual(result.toString(), expected); + }), + + it("toBase64: editableOption", () => { + const result = toBase64("some input", { + alphabet: { + value: "0-9A-W" + }, + }); + assert.strictEqual(result.toString(), "SPI1R1T0"); + }), + + it("toBase64: editableOptions key is value", () => { + const result = toBase64("some input", { + alphabet: "0-9A-W", + }); + assert.strictEqual(result.toString(), "SPI1R1T0"); + }), + + it("toBase64: editableOptions default", () => { + const result = toBase64("some input"); + assert.strictEqual(result.toString(), "c29tZSBpbnB1dA=="); + }), + + it("To BCD", () => { + assert.strictEqual(chef.toBCD("443").toString(), "0100 0100 0011"); + }), + + it("To CamelCase", () => { + assert.strictEqual(chef.toCamelCase("Quickest Wheel").toString(), "quickestWheel"); + }), + + it("toHex: accepts args", () => { + const result = toHex("some input", { + delimiter: "Colon", + }); + assert.strictEqual(result.toString(), "73:6f:6d:65:20:69:6e:70:75:74"); + }), + + it("To Kebab case", () => { + assert.strictEqual(chef.toKebabCase("Elfin Gold").toString(), "elfin-gold"); + }), + + it("To punycode", () => { + assert.strictEqual(chef.toPunycode("♠ ♣ ♥ ♦ ← ↑ ‍ →").toString(), " -m06cw7klao368lfb3aq"); + }), + + it("to snake case", () => { + assert.strictEqual(chef.toSnakeCase("Abhorrent Grass").value, "abhorrent_grass"); + }), + + it("to unix timestamp", () => { + assert.strictEqual(chef.toUNIXTimestamp("04-01-2001").toString(), "986083200 (Sun 1 April 2001 00:00:00 UTC)"); + }), + + it("Translate DateTime format", () => { + assert.strictEqual(chef.translateDateTimeFormat("01/04/1999 22:33:01").toString(), "Thursday 1st April 1999 22:33:01 +00:00 UTC"); + }), + + it("Triple DES encrypt / decrypt", () => { + assert.strictEqual( + chef.tripleDESDecrypt( + chef.tripleDESEncrypt("Destroy Money", {key: {string: "30 31 2f 30 34 2f 31 39 39 39 20 32 32 3a 33 33 3a 30 3130 31 2f 30 34", option: "Hex"}}), + {key: {string: "30 31 2f 30 34 2f 31 39 39 39 20 32 32 3a 33 33 3a 30 3130 31 2f 30 34", option: "Hex"}}).toString(), + "Destroy Money"); + }), + + it("UNIX Timestamp to Windows Filetime", () => { + assert.strictEqual(chef.UNIXTimestampToWindowsFiletime("2020735").toString(), "116464943350000000"); + }), + + it("XML Beautify", () => { + assert.strictEqual( + chef.XMLBeautify("abc").toString(), + ` +\\tabc +`); + }), + + it("XOR: toggleString with default option", () => { + assert.strictEqual(chef.XOR("fe023da5", { + key: "73 6f 6d 65" + }).toString(), + "\u0015\n]W@\u000b\fP"); + }), + + it("XOR: toggleString with custom option", () => { + assert.strictEqual(chef.XOR("fe023da5", { + key: { + string: "73 6f 6d 65", + option: "utf8", + } + }).toString(), + "QV\u0010\u0004UDWQ"); + }), + + it("XPath expression", () => { + assert.strictEqual( + chef.XPathExpression("abc", {xPath: "contact-info/company"}).toString(), + "abc"); + }), + + it("Zlib deflate / inflate", () => { + assert.strictEqual(chef.zlibInflate(chef.zlibDeflate("cut homer wile rooky grits dizen")).toString(), "cut homer wile rooky grits dizen"); + }), + + it("extract EXIF", () => { + assert.strictEqual( + chef.extractEXIF(fs.readFileSync("tests/node/sampleData/pic.jpg")).toString(), + `Found 7 tags. + +Orientation: 1 +XResolution: 72 +YResolution: 72 +ResolutionUnit: 2 +ColorSpace: 1 +ExifImageWidth: 57 +ExifImageHeight: 57`); + }), + + it("Tar", () => { + const tarred = chef.tar("some file content", { + filename: "test.txt" + }); + assert.strictEqual(tarred.type, 7); + assert.strictEqual(tarred.value.size, 2048); + assert.strictEqual(tarred.value.data.toString().substr(0, 8), "test.txt"); + }), + + it("Untar", () => { + const tarred = chef.tar("some file content", { + filename: "filename.txt", + }); + const untarred = chef.untar(tarred); + assert.strictEqual(untarred.type, 8); + assert.strictEqual(untarred.value.length, 1); + assert.strictEqual(untarred.value[0].name, "filename.txt"); + assert.strictEqual(untarred.value[0].data.toString(), "some file content"); + }), + + it("Zip", () => { + const zipped = chef.zip("some file content", { + filename: "sample.zip", + comment: "added", + operaringSystem: "Unix", + }); + + assert.strictEqual(zipped.type, 7); + assert.ok(zipped.value.data.toString().includes("sample.zip")); + assert.ok(zipped.value.data.toString().includes("added")); + }), + + it("Unzip", () => { + const zipped = chef.zip("some file content", { + filename: "zipped.zip", + comment: "zippy", + }); + const unzipped = chef.unzip(zipped); + + assert.equal(unzipped.type, 8); + assert.equal(unzipped.value[0].data, "some file content"); + assert.equal(unzipped.value[0].name, "zipped.zip"); + }), + + it("Unzip with password", () => { + const zipped = chef.zip("some content", { + password: "abcd", + }); + const unzipped = chef.unzip(zipped, { + password: "abcd", + }); + + assert.equal(unzipped.value[0].data, "some content"); + }), + +]); + diff --git a/tests/operations/Dish.mjs b/tests/operations/Dish.mjs new file mode 100644 index 00000000..c62ed391 --- /dev/null +++ b/tests/operations/Dish.mjs @@ -0,0 +1,12 @@ +import TestRegister from "../../lib/TestRegister.mjs"; +import Dish from "../../src/core/Dish.mjs"; +import it from "../node/assertionHandler.mjs"; +import assert from "assert"; + +TestRegister.addApiTests([ + it("Dish - presentAs: should exist", () => { + const dish = new Dish(); + assert(dish.presentAs); + }), + +]); diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index e107ac97..fc8d978e 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -11,18 +11,12 @@ * @license Apache-2.0 */ -// Define global environment functions -global.ENVIRONMENT_IS_WORKER = function() { - return typeof importScripts === "function"; -}; -global.ENVIRONMENT_IS_NODE = function() { - return typeof process === "object" && typeof require === "function"; -}; -global.ENVIRONMENT_IS_WEB = function() { - return typeof window === "object"; -}; +import { + setLongTestFailure, + logTestReport, +} from "../lib/utils"; -import TestRegister from "./TestRegister"; +import TestRegister from "../lib/TestRegister.mjs"; import "./tests/BCD"; import "./tests/BSON"; import "./tests/Base58"; @@ -49,9 +43,11 @@ import "./tests/Hash"; import "./tests/HaversineDistance"; import "./tests/Hexdump"; import "./tests/Image"; +import "./tests/IndexOfCoincidence"; import "./tests/Jump"; import "./tests/JSONBeautify"; import "./tests/JSONMinify"; +import "./tests/JSONtoCSV"; import "./tests/JWTDecode"; import "./tests/JWTSign"; import "./tests/JWTVerify"; @@ -95,81 +91,20 @@ import "./tests/ParseSSHHostKey"; // Cannot test operations that use the File type yet //import "./tests/SplitColourChannels"; -let allTestsPassing = true; -const testStatusCounts = { - total: 0, +// import "./tests/nodeApi/nodeApi"; +// import "./tests/nodeApi/ops"; + +const testStatus = { + allTestsPassing: true, + counts: { + total: 0, + } }; +setLongTestFailure(); -/** - * Helper function to convert a status to an icon. - * - * @param {string} status - * @returns {string} - */ -function statusToIcon(status) { - const icons = { - erroring: "🔥", - failing: "❌", - passing: "✔️️", - }; - return icons[status] || "?"; -} - - -/** - * Displays a given test result in the console. - * - * @param {Object} testResult - */ -function handleTestResult(testResult) { - allTestsPassing = allTestsPassing && testResult.status === "passing"; - const newCount = (testStatusCounts[testResult.status] || 0) + 1; - testStatusCounts[testResult.status] = newCount; - testStatusCounts.total += 1; - - console.log([ - statusToIcon(testResult.status), - testResult.test.name - ].join(" ")); - - if (testResult.output) { - console.log( - testResult.output - .trim() - .replace(/^/, "\t") - .replace(/\n/g, "\n\t") - ); - } -} - - -/** - * Fail if the process takes longer than 60 seconds. - */ -setTimeout(function() { - console.log("Tests took longer than 60 seconds to run, returning."); - process.exit(1); -}, 60 * 1000); - +const logOpsTestReport = logTestReport.bind(null, testStatus); TestRegister.runTests() - .then(function(results) { - results.forEach(handleTestResult); + .then(logOpsTestReport); - console.log("\n"); - - for (const testStatus in testStatusCounts) { - const count = testStatusCounts[testStatus]; - if (count > 0) { - console.log(testStatus.toUpperCase(), count); - } - } - - if (!allTestsPassing) { - console.log("\nFailing tests:\n"); - results.filter(r => r.status !== "passing").forEach(handleTestResult); - } - - process.exit(allTestsPassing ? 0 : 1); - }); diff --git a/tests/operations/tests/BCD.mjs b/tests/operations/tests/BCD.mjs index 87cbd53e..c6715e56 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/BLAKE2b.mjs b/tests/operations/tests/BLAKE2b.mjs index 94c4fab9..088b1078 100644 --- a/tests/operations/tests/BLAKE2b.mjs +++ b/tests/operations/tests/BLAKE2b.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2019 * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/BLAKE2s.mjs b/tests/operations/tests/BLAKE2s.mjs index 09d53b6a..fe978d24 100755 --- a/tests/operations/tests/BLAKE2s.mjs +++ b/tests/operations/tests/BLAKE2s.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2019 * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/BSON.mjs b/tests/operations/tests/BSON.mjs index f9eb6a87..83ef7caa 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Base58.mjs b/tests/operations/tests/Base58.mjs index 0cc7612c..c23ba9ab 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Base62.mjs b/tests/operations/tests/Base62.mjs index bb98348b..defe5fa5 100644 --- a/tests/operations/tests/Base62.mjs +++ b/tests/operations/tests/Base62.mjs @@ -7,7 +7,7 @@ * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Base64.mjs b/tests/operations/tests/Base64.mjs index d48c93c6..6e6fa703 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; const ALL_BYTES = [ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", diff --git a/tests/operations/tests/BitwiseOp.mjs b/tests/operations/tests/BitwiseOp.mjs index 09af70a1..50303677 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Bombe.mjs b/tests/operations/tests/Bombe.mjs index b44e032c..2d391211 100644 --- a/tests/operations/tests/Bombe.mjs +++ b/tests/operations/tests/Bombe.mjs @@ -4,7 +4,7 @@ * @copyright Crown Copyright 2019 * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/ByteRepr.mjs b/tests/operations/tests/ByteRepr.mjs index 86b6d58d..51d9604d 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; const ALL_BYTES = [ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", diff --git a/tests/operations/tests/CSV.mjs b/tests/operations/tests/CSV.mjs index 28610058..cc3535a4 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; const EXAMPLE_CSV = `A,B,C,D,E,F\r 1,2,3,4,5,6\r diff --git a/tests/operations/tests/CartesianProduct.mjs b/tests/operations/tests/CartesianProduct.mjs index cb89ac5b..597a8450 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/CharEnc.mjs b/tests/operations/tests/CharEnc.mjs index d154ca3e..552d31b3 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Charts.mjs b/tests/operations/tests/Charts.mjs index 5b6d9781..a62b5741 100644 --- a/tests/operations/tests/Charts.mjs +++ b/tests/operations/tests/Charts.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2019 * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Checksum.mjs b/tests/operations/tests/Checksum.mjs index 9be19495..142ee267 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; const BASIC_STRING = "The ships hung in the sky in much the same way that bricks don't."; const UTF8_STR = "ნუ პანიკას"; @@ -29,6 +29,127 @@ const ALL_BYTES = [ ].join(""); TestRegister.addTests([ + { + name: "CRC-8: nothing", + input: "", + expectedOutput: "00", + recipeConfig: [ + { + "op": "CRC-8 Checksum", + "args": ["CRC-8"] + } + ] + }, + { + name: "CRC-8: default check", + input: "123456789", + expectedOutput: "f4", + recipeConfig: [ + { + "op": "CRC-8 Checksum", + "args": ["CRC-8"] + } + ] + }, + { + name: "CRC-8: CDMA2000", + input: "123456789", + expectedOutput: "da", + recipeConfig: [ + { + "op": "CRC-8 Checksum", + "args": ["CRC-8/CDMA2000"] + } + ] + }, + { + name: "CRC-8: DARC", + input: "123456789", + expectedOutput: "15", + recipeConfig: [ + { + "op": "CRC-8 Checksum", + "args": ["CRC-8/DARC"] + } + ] + }, + { + name: "CRC-8: DVB-S2", + input: "123456789", + expectedOutput: "bc", + recipeConfig: [ + { + "op": "CRC-8 Checksum", + "args": ["CRC-8/DVB-S2"] + } + ] + }, + { + name: "CRC-8: EBU", + input: "123456789", + expectedOutput: "97", + recipeConfig: [ + { + "op": "CRC-8 Checksum", + "args": ["CRC-8/EBU"] + } + ] + }, + { + name: "CRC-8: I-CODE", + input: "123456789", + expectedOutput: "7e", + recipeConfig: [ + { + "op": "CRC-8 Checksum", + "args": ["CRC-8/I-CODE"] + } + ] + }, + { + name: "CRC-8: ITU", + input: "123456789", + expectedOutput: "a1", + recipeConfig: [ + { + "op": "CRC-8 Checksum", + "args": ["CRC-8/ITU"] + } + ] + }, + { + name: "CRC-8: MAXIM", + input: "123456789", + expectedOutput: "a1", + recipeConfig: [ + { + "op": "CRC-8 Checksum", + "args": ["CRC-8/MAXIM"] + } + ] + }, + { + name: "CRC-8: ROHC", + input: "123456789", + expectedOutput: "d0", + recipeConfig: [ + { + "op": "CRC-8 Checksum", + "args": ["CRC-8/ROHC"] + } + ] + }, + { + name: "CRC-8: WCDMA", + input: "123456789", + expectedOutput: "25", + recipeConfig: [ + { + "op": "CRC-8 Checksum", + "args": ["CRC-8/WCDMA"] + } + ] + }, { name: "CRC-16: nothing", input: "", @@ -116,5 +237,5 @@ TestRegister.addTests([ "args": [] } ] - }, + } ]); diff --git a/tests/operations/tests/Ciphers.mjs b/tests/operations/tests/Ciphers.mjs index f806e553..62cbaa54 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ diff --git a/tests/operations/tests/Code.mjs b/tests/operations/tests/Code.mjs index 048e13d6..94179553 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; const JSON_TEST_DATA = { "store": { diff --git a/tests/operations/tests/Comment.mjs b/tests/operations/tests/Comment.mjs index 2c033f56..9fc80a63 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; const ALL_BYTES = [ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", diff --git a/tests/operations/tests/Compress.mjs b/tests/operations/tests/Compress.mjs index 877b7215..a1e895bb 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/ConditionalJump.mjs b/tests/operations/tests/ConditionalJump.mjs index b38c0d1b..6275f590 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/ConvertCoordinateFormat.mjs b/tests/operations/tests/ConvertCoordinateFormat.mjs index 1291aa4d..afe35d87 100644 --- a/tests/operations/tests/ConvertCoordinateFormat.mjs +++ b/tests/operations/tests/ConvertCoordinateFormat.mjs @@ -18,7 +18,7 @@ * UTM: 30N 699456 5709791, */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Crypt.mjs b/tests/operations/tests/Crypt.mjs index 60068ab3..82a622e0 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ /** @@ -209,9 +209,9 @@ Tag: 16a3e732a605cc9ca29108f742ca0743`, { name: "AES Encrypt: AES-128-GCM, Binary", input: "7a0e643132750e96d805d11e9e48e281fa39a41039286423cc1c045e5442b40bf1c3f2822bded3f9c8ef11cb25da64dda9c7ab87c246bd305385150c98f31465c2a6180fe81d31ea289b916504d5a12e1de26cb10adba84a0cb0c86f94bc14bc554f3018", - expectedOutput: `fa17fcbf5e8763322c1b0c8562e1512ed9d702ef70c1643572b9de3e34ae6b535e6c1b992432aa6d06fb6f80c861262aef66e7c26035afe77bd3861261e4e092b523f058f8ebef2143db21bc16d02f7a011efb07419300cb41c3b884d1d8d6a766b8963c + expectedOutput: `5a29debb5c5f38cdf8aee421bd94dbbf3399947faddf205f88b3ad8ecb0c51214ec0e28bf78942dfa212d7eb15259bbdcac677b4c05f473eeb9331d74f31d441d97d56eb5c73b586342d72128ca528813543dc0fc7eddb7477172cc9194c18b2e1383e4e -Tag: fa6bbb34c8cde65a3d7b93fb094fc84f`, +Tag: 70fad2ca19412c20f40fd06918736e56`, recipeConfig: [ { "op": "AES Encrypt", @@ -301,9 +301,9 @@ Tag: fa6bbb34c8cde65a3d7b93fb094fc84f`, { name: "AES Encrypt: AES-192-GCM, Binary", input: "7a0e643132750e96d805d11e9e48e281fa39a41039286423cc1c045e5442b40bf1c3f2822bded3f9c8ef11cb25da64dda9c7ab87c246bd305385150c98f31465c2a6180fe81d31ea289b916504d5a12e1de26cb10adba84a0cb0c86f94bc14bc554f3018", - expectedOutput: `ed22946f96964d300b45f5ce2d9601ba87682da1a603c90e6d4f7738729b0602f613ee392c9bfc7792594474f1213fb99185851f02ece4df0e93995e49f97aa4d0a337d7a80d83e4219dae5a3d36658f8659cdd5ed7c32707f98656fab7fb43f7a61e37c + expectedOutput: `318b479d919d506f0cd904f2676fab263a7921b6d7e0514f36e03ae2333b77fa66ef5600babcb2ee9718aeb71fc357412343c1f2cb351d8715bb0aedae4a6468124f9c4aaf6a721b306beddbe63a978bec8baeeba4b663be33ee5bc982746bd4aed1c38b -Tag: be17cb31edb77f648b9d1032b235b33d`, +Tag: 86db597d5302595223cadbd990f1309b`, recipeConfig: [ { "op": "AES Encrypt", @@ -393,9 +393,9 @@ Tag: be17cb31edb77f648b9d1032b235b33d`, { name: "AES Encrypt: AES-256-GCM, Binary", input: "7a0e643132750e96d805d11e9e48e281fa39a41039286423cc1c045e5442b40bf1c3f2822bded3f9c8ef11cb25da64dda9c7ab87c246bd305385150c98f31465c2a6180fe81d31ea289b916504d5a12e1de26cb10adba84a0cb0c86f94bc14bc554f3018", - expectedOutput: `e3f1b236eaf3b9df69df8133a1b417fa42b242d8ad49e4d2f3469aca7e2a41737e4f2c8a0d212143287088fad51743577dc6dfa8ed328ca90113cbeb9b137926b2168cc037bdc371777e6ee02b9d9c017b6054fd83d43b4885fbe9c044a8574f1491a893 + expectedOutput: `1287f188ad4d7ab0d9ff69b3c29cb11f861389532d8cb9337181da2e8cfc74a84927e8c0dd7a28a32fd485afe694259a63c199b199b95edd87c7aa95329feac340f2b78b72956a85f367044d821766b1b7135815571df44900695f1518cf3ae38ecb650f -Tag: 23ddbd3ee4de33f98a9ea9a170bdf268`, +Tag: 821b1e5f32dad052e502775a523d957a`, recipeConfig: [ { "op": "AES Encrypt", diff --git a/tests/operations/tests/DateTime.mjs b/tests/operations/tests/DateTime.mjs index 3063ca64..6276890d 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Enigma.mjs b/tests/operations/tests/Enigma.mjs index 5a3a8002..090ecc35 100644 --- a/tests/operations/tests/Enigma.mjs +++ b/tests/operations/tests/Enigma.mjs @@ -4,7 +4,7 @@ * @copyright Crown Copyright 2019 * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/ExtractEmailAddresses.mjs b/tests/operations/tests/ExtractEmailAddresses.mjs index 59365828..a0a01f67 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Fork.mjs b/tests/operations/tests/Fork.mjs index 3752e340..d83d6262 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/FromDecimal.mjs b/tests/operations/tests/FromDecimal.mjs index b9b4de05..dfc440ec 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/FromGeohash.mjs b/tests/operations/tests/FromGeohash.mjs new file mode 100644 index 00000000..dec58687 --- /dev/null +++ b/tests/operations/tests/FromGeohash.mjs @@ -0,0 +1,55 @@ +/** + * To Geohash tests + * + * @author gchq77703 + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +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/tests/operations/tests/Hash.mjs b/tests/operations/tests/Hash.mjs index 31ebc3a6..9da5cfb6 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { @@ -1033,6 +1033,72 @@ TestRegister.addTests([ } ] }, + { + name: "Streebog-256: Test Case 1", + input: "", + expectedOutput: "3f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb", + recipeConfig: [ + { + op: "Streebog", + args: ["256"] + } + ] + }, + { + name: "Streebog-256: Test Case 2", + input: "The quick brown fox jumps over the lazy dog", + expectedOutput: "3e7dea7f2384b6c5a3d0e24aaa29c05e89ddd762145030ec22c71a6db8b2c1f4", + recipeConfig: [ + { + op: "Streebog", + args: ["256"] + } + ] + }, + { + name: "Streebog-512: Test Case 1", + input: "", + expectedOutput: "8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a", + recipeConfig: [ + { + op: "Streebog", + args: ["512"] + } + ] + }, + { + name: "Streebog-512: Test Case 2", + input: "The quick brown fox jumps over the lazy dog", + expectedOutput: "d2b793a0bb6cb5904828b5b6dcfb443bb8f33efc06ad09368878ae4cdc8245b97e60802469bed1e7c21a64ff0b179a6a1e0bb74d92965450a0adab69162c00fe", + recipeConfig: [ + { + op: "Streebog", + args: ["512"] + } + ] + }, + { + name: "GOST R 34.11-94: Test Case 1", + input: "", + expectedOutput: "981e5f3ca30c841487830f84fb433e13ac1101569b9c13584ac483234cd656c0", + recipeConfig: [ + { + op: "GOST hash", + args: ["D-A"] + } + ] + }, + { + name: "GOST R 34.11-94: Test Case 2", + input: "This is message, length=32 bytes", + expectedOutput: "2cefc2f7b7bdc514e18ea57fa74ff357e7fa17d652c75f69cb1be7893ede48eb", + recipeConfig: [ + { + op: "GOST hash", + args: ["D-A"] + } + ] + } /*{ // This takes a LONG time to run (over a minute usually). name: "Scrypt: RFC test vector 4", input: "pleaseletmein", diff --git a/tests/operations/tests/HaversineDistance.mjs b/tests/operations/tests/HaversineDistance.mjs index ef0a298e..94fc7289 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Hexdump.mjs b/tests/operations/tests/Hexdump.mjs index 6b5c5043..90523a08 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; const ALL_BYTES = [ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", diff --git a/tests/operations/tests/Image.mjs b/tests/operations/tests/Image.mjs index 44f0af71..e617578e 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/IndexOfCoincidence.mjs b/tests/operations/tests/IndexOfCoincidence.mjs new file mode 100644 index 00000000..647725fd --- /dev/null +++ b/tests/operations/tests/IndexOfCoincidence.mjs @@ -0,0 +1,22 @@ +/** + * Index of Coincidence tests. + * + * @author George O [georgeomnet+cyberchef@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "Index of Coincidence", + input: "Hello world, this is a test to determine the correct IC value.", + expectedMatch: /^Index of Coincidence: 0\.07142857142857142\nNormalized: 1\.857142857142857/, + recipeConfig: [ + { + "op": "Index of Coincidence", + "args": [] + }, + ], + }, +]); diff --git a/tests/operations/tests/JSONBeautify.mjs b/tests/operations/tests/JSONBeautify.mjs index 4388f82b..11578678 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/JSONMinify.mjs b/tests/operations/tests/JSONMinify.mjs index dab396f1..93ab8123 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/JSONtoCSV.mjs b/tests/operations/tests/JSONtoCSV.mjs new file mode 100644 index 00000000..195bce3d --- /dev/null +++ b/tests/operations/tests/JSONtoCSV.mjs @@ -0,0 +1,93 @@ +/** + * JSON to CSV tests. + * + * @author mshwed [m@ttshwed.com] + * + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +const EXPECTED_CSV_SINGLE = "a,b,c\r\n1,2,3\r\n"; +const EXPECTED_CSV_MULTIPLE = "a,b,c\r\n1,2,3\r\n1,2,3\r\n"; +const EXPECTED_CSV_EMPTY = "\r\n\r\n"; + +TestRegister.addTests([ + { + name: "JSON to CSV: strings as values", + input: JSON.stringify({a: "1", b: "2", c: "3"}), + expectedOutput: EXPECTED_CSV_SINGLE, + recipeConfig: [ + { + op: "JSON to CSV", + args: [",", "\\r\\n"] + }, + ], + }, + { + name: "JSON to CSV: numbers as values", + input: JSON.stringify({a: 1, b: 2, c: 3}), + expectedOutput: EXPECTED_CSV_SINGLE, + recipeConfig: [ + { + op: "JSON to CSV", + args: [",", "\\r\\n"] + }, + ], + }, + { + name: "JSON to CSV: numbers and strings as values", + input: JSON.stringify({a: 1, b: "2", c: 3}), + expectedOutput: EXPECTED_CSV_SINGLE, + recipeConfig: [ + { + op: "JSON to CSV", + args: [",", "\\r\\n"] + }, + ], + }, + { + name: "JSON to CSV: JSON as an array", + input: JSON.stringify([{a: 1, b: "2", c: 3}]), + expectedOutput: EXPECTED_CSV_SINGLE, + recipeConfig: [ + { + op: "JSON to CSV", + args: [",", "\\r\\n"] + }, + ], + }, + { + name: "JSON to CSV: multiple JSON values in an array", + input: JSON.stringify([{a: 1, b: "2", c: 3}, {a: 1, b: "2", c: 3}]), + expectedOutput: EXPECTED_CSV_MULTIPLE, + recipeConfig: [ + { + op: "JSON to CSV", + args: [",", "\\r\\n"] + }, + ], + }, + { + name: "JSON to CSV: empty JSON", + input: JSON.stringify({}), + expectedOutput: EXPECTED_CSV_EMPTY, + recipeConfig: [ + { + op: "JSON to CSV", + args: [",", "\\r\\n"] + }, + ], + }, + { + name: "JSON to CSV: empty JSON in array", + input: JSON.stringify([{}]), + expectedOutput: EXPECTED_CSV_EMPTY, + recipeConfig: [ + { + op: "JSON to CSV", + args: [",", "\\r\\n"] + }, + ], + } +]); diff --git a/tests/operations/tests/JWTDecode.mjs b/tests/operations/tests/JWTDecode.mjs index df9b9bcc..1ef47f81 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; const outputObject = JSON.stringify({ String: "SomeString", diff --git a/tests/operations/tests/JWTSign.mjs b/tests/operations/tests/JWTSign.mjs index 69787712..3970a5e5 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; const inputObject = JSON.stringify({ String: "SomeString", diff --git a/tests/operations/tests/JWTVerify.mjs b/tests/operations/tests/JWTVerify.mjs index 52b8574b..f9944f6b 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; const outputObject = JSON.stringify({ String: "SomeString", diff --git a/tests/operations/tests/Jump.mjs b/tests/operations/tests/Jump.mjs index 9bc98f31..4a1d91b0 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/MS.mjs b/tests/operations/tests/MS.mjs index 01618d90..5862e430 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Magic.mjs b/tests/operations/tests/Magic.mjs index b7fd5ca3..d168c92d 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ @@ -34,7 +34,7 @@ TestRegister.addTests([ }, { name: "Magic: jpeg", - input: "\xFF\xD8\xFF", + input: "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x01\x00\x48\x00\x48\x00\x00\xff\xdb\x00\x43\x00\x03\x02\x02\x03\x02\x02\x03\x03\x03\x03\x04\x03\x03\x04\x05\x08\x05\x05\x04\x04\x05\x0a\x07\x07\x06\x08\x0c\x0a\x0c\x0c\x0b\x0a\x0b\x0b\x0d\x0e\x12\x10\x0d\x0e\x11\x0e\x0b\x0b\x10\x16\x10\x11\x13\x14\x15\x15\x15\x0c\x0f\x17\x18\x16\x14\x18\x12\x14\x15\x14\xff\xdb\x00\x43\x01\x03\x04\x04\x05\x04\x05\x09\x05\x05\x09\x14\x0d\x0b\x0d\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\xff\xc2\x00\x11\x08\x00\x32\x00\x32\x03\x01\x11\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1c\x00\x00\x02\x02\x03\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x07\x04\x05\x00\x02\x03\x01\x08\xff\xc4\x00\x1a\x01\x00\x03\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x05\x06\x03\x02\x01\x07\xff\xda\x00\x0c\x03\x01\x00\x02\x10\x03\x10\x00\x00\x01\xe7\x14\xd4\x6e\x92\x6e\xe0\x67\xd3\x01\x2e\xe2\x61\x94\xcc\xb8\xa2\x6a\x9e\xa0\x8a\x71\xfa\xde\xdc\x52\xd3\xd5\x89\x16\xe1\xbd\x57\x6b\x4c\xc9\xc9\x02\xad\xcd\x3f\x3d\xe3\x0e\xfc\xcd\x61\x95\xe9\x08\xa8\x74\x05\x4e\xc3\x14\x07\x5d\x62\xc5\x22\x57\xb8\x0f\xbd\x49\xd9\x50\xd2\x6b\x46\x3c\xe3\x8f\xa1\xd5\x83\x2d\x27\xa5\xbd\xf3\x24\x99\x82\x11\xf5\xd5\xf3\xf0\x05\x8f\x34\xbc\x16\xa5\x83\x00\x74\xaa\x86\x66\xb4\x8e\xf2\x89\x13\x88\xd9\xe2\x16\x56\xe5\xb3\x4a\x8c\xc9\xc9\x61\xa1\x95\xa5\x1d\x7e\xb8\xa8\x67\x65\xf3\xbd\xa4\xa7\xff\xc4\x00\x21\x10\x00\x02\x03\x00\x02\x02\x02\x03\x00\x00\x00\x00\x00\x00\x00\x00\x03\x04\x01\x02\x05\x00\x06\x11\x12\x13\x14\x16\x21\x24\xff\xda\x00\x08\x01\x01\x00\x01\x05\x02\xd9\xa5\x49\x5a\x1d\x6a\x39\xf8\xfa\xd5\x03\x98\xe6\x61\x44\x16\xa2\xc9\x82\xb4\xbc\x97\xae\x9e\xe4\x63\xec\x33\xd6\x24\x4b\x29\xa4\x98\xad\x5c\x4a\x68\xae\x4e\x5e\x41\x3c\x3f\xc8\x20\x0c\x4b\xc8\xd4\x42\x34\xb8\x7c\x2f\x3a\xd1\xea\x3a\x36\x19\xf4\x4d\x42\xd9\x41\xe9\x02\xc5\x8d\xbc\xd1\x45\xeb\xf5\x12\xcc\xb3\x9b\xc0\x8b\x17\x23\x99\x8e\xad\x9a\x67\xb7\x46\x65\x35\xd6\xa5\x9c\x81\xf8\x8b\x5e\x9f\x2e\x83\x4c\x35\xcd\xb3\x83\x45\x1c\xe1\xd9\x85\x35\xf3\x1c\xd2\x73\xad\x3a\x1a\xbd\x67\x87\xed\x89\x96\x96\x79\xb6\x11\x45\x9c\x9e\xb3\x03\x9d\x0c\x77\x06\xf2\xb8\x9b\x9f\x0b\x5a\x42\xaa\x4b\x58\xa4\xf6\x14\x45\xf4\x3b\x1d\x2a\x4c\x6c\xe5\x82\xba\x7d\x8f\xf9\x91\xcf\xa5\x46\xa6\xbf\xeb\x12\x0d\x7f\x1f\xff\xc4\x00\x2f\x11\x00\x01\x04\x01\x02\x03\x06\x04\x07\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x03\x04\x11\x12\x31\x05\x13\x21\x14\x22\x41\x51\xa1\xf0\x32\x42\x71\x81\x23\x24\x52\x53\x61\x91\xf1\xff\xda\x00\x08\x01\x03\x01\x01\x3f\x01\xbf\x0e\x98\xc3\xc0\xfb\xa1\x19\x71\xca\xa8\x5b\x5d\xed\x73\xd3\x38\xe3\x98\xf7\x35\xad\x1a\x73\xfc\xa7\xcf\xda\x7b\xce\xf1\x4d\xb0\x21\x69\x0d\x0b\x4b\xcf\xce\x54\x5f\x98\xac\x1b\x28\xdf\x1f\xd6\x56\x98\x23\x9a\x37\x39\xbd\x4e\xde\xfd\x17\x16\xae\xd7\x5a\xdf\xa1\xea\x9d\x0c\x6d\x6e\x85\x2b\x66\x2d\x0d\x8c\x74\x5c\x2a\x06\x13\x87\x0c\xe1\x3d\xa0\x38\x85\x74\x4d\x5a\x00\xe8\xc6\xdf\x4f\xa2\xb1\x62\x6b\x04\x03\xd7\xc9\x43\x34\x92\x3f\x4b\xdd\x9c\xa6\x70\xfa\xad\xf9\x7b\xde\x6a\xd5\xc8\xe0\x7e\x83\xb8\x50\xdb\x9f\x99\xad\x87\x01\x3a\xce\xb7\x17\x13\xba\x3a\x2d\xf0\xf1\x8d\xb4\xfa\xff\x00\xaa\x0a\xd6\x2a\x1e\x7e\x94\x6a\x56\xbc\xe6\xcf\x19\xeb\xef\xd5\x5d\x12\x4b\x16\x23\x2a\xd5\x52\x23\x2e\x27\x38\x5c\x3e\x28\xa5\x92\x31\x27\xc3\xe2\xb1\x18\xe8\x1a\x14\x1c\x4a\x0a\xf0\xb7\x4e\xe0\x6c\xac\xf1\x37\xd8\xee\xe3\xba\x9c\x49\x87\x9b\x0e\xe3\x65\xda\x6c\xb2\x42\xf2\xed\xf7\x54\xa4\x80\xc4\xd9\x2c\x63\x27\xcf\x65\xc4\xed\x40\xf7\x35\x91\xbb\xa8\x59\x72\xbf\x15\x53\x8e\x46\xfe\xfe\xcb\x46\x32\x15\x2c\xb4\x38\x65\x5c\x8f\x97\x28\x78\xf1\xea\xa4\xa3\xda\x86\xac\xe1\x4b\xc3\xa1\x90\x08\x88\xc1\xf3\x43\x81\xd2\xfd\xc3\xe8\x9d\xf1\x39\x40\x01\x99\x80\xf9\x85\x60\x72\xec\x38\x33\xa7\x54\xd3\xf8\x79\x55\x09\x70\x76\xa5\x6f\x67\x95\xce\x93\xf5\x15\xff\xc4\x00\x2f\x11\x00\x01\x03\x02\x03\x06\x05\x03\x05\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x03\x04\x21\x11\x13\x31\x05\x12\x22\x41\x51\x61\x71\xa1\xb1\xc1\xf0\x23\x32\xd1\x33\x81\x91\xe1\xf1\xff\xda\x00\x08\x01\x02\x01\x01\x3f\x01\xd9\x04\xef\x90\x2c\x3d\x4e\x1e\xca\xb2\x67\xc2\xc0\x19\x70\xa4\x95\xc6\x1e\x23\x74\xd9\xef\x8b\xee\x70\xbe\x9a\x76\xfc\x2a\x9a\x82\xe9\x4e\x04\x80\xb3\x09\xb3\x8e\x3e\x3e\x48\x6d\x9d\xdb\x64\xb7\xf8\x51\xc2\xd8\x2b\xfe\x96\x83\x1f\x45\x0b\xf8\xae\xab\x33\x62\x95\xed\x6d\xef\xeb\xa7\x9d\x94\x59\xd2\xcb\x8b\xec\x42\x73\x23\x67\x2f\x9c\x96\xc9\xa5\x8a\x79\x1c\x65\x18\xf0\xd9\x09\x48\x18\x0f\x45\x43\x4d\x9b\x2b\xa4\x71\xd7\x45\x2e\xfc\x2e\x31\xcc\x6c\x54\xad\x8a\x30\x66\x2d\xd1\x06\xc4\x00\x99\xfa\x15\x56\x59\x39\x73\xe0\x1c\x3e\xea\x16\xcf\x01\xce\x88\x1b\xe9\xd1\x3f\x67\x4a\xf7\x17\x11\xaa\x86\x4d\xe3\xbc\x3a\xa9\xaa\x29\x6a\x1c\x23\x79\xfd\xd5\x7d\x29\x7c\x6e\x11\x1c\x07\x44\xfa\x82\xfd\xd1\x26\x16\xeb\xa2\x64\x9c\x43\x28\x6e\xe2\xa1\x9c\xc4\xe9\x1b\x25\xc0\xbf\x6d\x7c\xb5\x42\x68\xb0\xfb\xbd\x11\x95\xad\xa7\xc8\xe6\xa9\xa9\x44\xd8\x1c\x78\x8f\x92\xa7\x7e\x55\x66\x4c\xda\x1d\x7a\x76\xf9\xe2\xa4\xa3\x8a\x46\x6e\x39\xbf\x31\x54\xf1\x06\xcd\x23\x59\x89\x23\x9f\x8f\xf7\x8e\x2a\xbd\x8d\x8f\x8d\xd6\xd2\xc8\xe4\x63\xa9\x55\x55\x70\xc8\x04\xad\x07\xba\xa6\xaa\x19\x8d\x7b\x6c\xb6\xa3\x9b\x23\x98\xe0\xdf\x9e\xfa\xaa\x08\xc1\xa7\x11\xbd\x45\x3c\x9b\x29\xfb\xae\x18\xa3\x2b\xaa\x5f\xbe\xe3\xa9\xd3\xb7\xfa\x8e\xcd\x18\xd9\x3b\x90\xef\xf9\x54\xc4\x87\x38\x8e\x40\xa9\x1e\xf7\x81\xbc\x71\xb7\xb2\xd9\xd2\xbc\xd8\xb9\x48\xf7\x1a\x9b\x9e\xbe\xaa\x8f\xf5\x23\xf1\xf7\x47\x55\xff\xc4\x00\x2f\x10\x00\x02\x01\x03\x02\x03\x07\x02\x07\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x00\x04\x11\x12\x21\x13\x22\x31\x23\x32\x41\x51\x61\x71\x91\x33\xf0\x05\x15\x42\x52\x72\x81\xb1\xa1\xff\xda\x00\x08\x01\x01\x00\x06\x3f\x02\x4d\x71\xf2\xc8\x74\x97\x23\x97\xd0\x1f\x9a\x4b\x62\x0e\x95\x50\x4c\x63\x65\xd2\x69\xa5\xe0\x17\x94\x12\xe3\x9b\x53\x1f\x21\x4b\x24\x7a\x56\xed\x54\x1d\x12\xf4\xff\x00\x95\x1c\x57\x3c\x27\x98\x13\x92\x17\x6c\xe6\x99\x63\x0a\x90\xb0\xea\xa3\x07\x39\xde\x9d\xbf\x36\xbb\x19\x39\xc6\x05\x42\x67\x71\x3c\x93\x91\xac\x28\xdb\x3a\xaa\x07\xe1\x2c\x4c\xd9\x8f\x5f\x4e\xa3\x1b\x8a\x33\x63\xe8\xe7\x58\xf4\xf3\xf8\xa8\x73\x20\x3a\xfa\x11\xb8\xac\xb3\xa9\x45\xc9\x5e\x7d\xab\x07\x94\xe0\xb0\x22\x97\x55\xc2\x83\x8d\xc6\xa1\x56\xb6\x82\xec\xdb\x88\x70\x57\x4c\x7f\x53\x02\xb8\xd7\x40\xf6\x03\x66\x3d\xd6\xf5\xac\x22\x95\x4e\xb8\x5e\x95\xa9\x53\x97\x57\xd3\x45\xf5\xa6\x78\xad\x9c\xc5\xfb\x54\xe9\xfe\xfd\x29\xa0\x96\xe9\x4d\xc4\x6b\xd3\xc4\x7b\xd0\x4f\xdb\xb6\xf4\x62\x90\x88\xe5\x83\x1d\x5b\x1c\xc3\xd6\x83\x7e\x22\xdc\x18\x50\x03\x11\x55\xc3\xb1\xf3\x7f\xb1\x53\x76\x41\xb3\xe6\xdd\x3c\x32\x2a\x49\xa6\xb7\x6b\xce\x1a\xe1\x44\x60\x64\x93\xef\x5d\x94\x26\xd2\x72\x46\x1c\x91\x81\xbf\xd8\xa5\x62\x07\x1e\xe0\xe0\xe8\x18\xd7\x81\x9a\xe8\xc3\xd3\x02\x96\x6e\x49\x84\x8e\x18\x37\x9f\xf7\x49\xca\xca\x9a\x87\x67\xe0\x7d\xea\xde\xe4\x8d\x06\x16\xd5\x86\x3d\xdf\x03\x51\x49\x12\x34\x90\xe7\x02\x42\xa7\x07\xcf\x7a\x71\x15\xbb\x5c\x5a\xa0\x05\x94\x0c\x8c\xef\xb9\xfb\xf0\xa2\xef\x6c\xd2\x28\xe5\x17\x19\xc2\xc4\xd8\xe9\x83\xd7\x3b\x6e\x28\xf6\xac\x3d\xa8\x8b\xbb\x6e\xcc\x6e\x22\xea\x99\xf3\xa7\x9a\x2b\x45\x8d\x8a\xe5\x4a\xae\x9c\x7c\x55\xd6\x26\x26\x44\x8b\x05\x41\xca\x82\x4f\xfb\xb0\xa9\x97\xaa\x89\x0c\x40\x8d\xb4\x8f\xbc\xd4\xb1\xc2\x99\xfd\x32\x06\x3f\x18\xab\xbb\x9b\x7d\x1c\x69\x4e\xa7\x8c\xf7\x4e\xfb\xd1\xc4\x7b\x7b\x1a\x80\x1d\xc7\x1b\xa1\xfe\x55\x74\xac\x03\x29\x2a\x30\x7f\x98\xa6\x68\xa2\x48\xd8\xb9\xc9\x45\xc7\xeb\xa9\x38\x5d\x96\x48\xee\x6d\xe1\x50\xba\xa8\x57\x6d\x1a\x98\x0d\xcf\x66\xb5\x70\x06\xc3\x82\xdf\xe5\x77\xdb\xe6\xbf\xff\xc4\x00\x22\x10\x01\x00\x02\x02\x02\x03\x00\x03\x01\x01\x00\x00\x00\x00\x00\x00\x01\x11\x21\x00\x31\x41\x51\x61\x71\x81\x91\xa1\xb1\xc1\xf0\xff\xda\x00\x08\x01\x01\x00\x01\x3f\x21\x10\x32\xcf\xff\x00\x55\x6d\x07\xd7\x39\x55\x4c\x4f\x10\x53\x39\x7d\x17\x25\x21\xb4\x98\xf1\x0e\xb7\xde\x3e\x30\xd6\x2b\xcd\xeb\x1f\x75\x91\x1d\x35\x56\x4e\xce\x9c\x8a\x38\xb5\xaa\x29\xa9\xa2\xf0\x19\x37\xe0\xef\x5a\xc4\x09\x6c\x1f\x40\xf9\x13\xf7\x26\x8e\x52\xf0\x50\x51\xb9\x8b\xc0\xb0\x23\xe9\x2f\xda\xdc\x6f\x1d\x93\x08\x40\xa5\xdc\xf5\x94\xb2\x20\x4b\x48\xee\x3b\xc1\x3a\x59\x1a\x13\x5c\xfa\xe3\x1c\xd5\x99\x0a\x1c\x9a\xc2\xb0\x09\x24\xdd\x77\xee\x30\x2c\xeb\x99\x13\x50\x0e\x89\xdc\x34\x67\x1f\x94\xb8\xf6\x4e\xb2\x5e\x0d\x8c\x21\xf0\x0f\xb5\x8e\x6c\xa2\x01\x86\x9e\xd8\x24\xc4\x93\x28\xd0\xa3\x9a\x7f\x78\x4d\x28\xc6\x04\xd6\x21\xd1\x28\x94\x1c\xcf\xd1\x33\xcd\x63\x8b\x38\x94\xea\xce\x0a\x88\x2c\xde\xae\x12\x19\x8c\x3c\x38\x17\x61\xeb\x23\x79\x0d\x51\x5c\x40\xfa\xb8\x72\x01\xe3\x03\x0b\xee\x78\x79\x3c\x60\x5b\xb8\xe4\x38\xb7\xbd\x18\x20\x57\x3c\x3c\x4c\xa8\x62\xd5\xeb\x97\x00\x63\xc0\x83\xd1\xdd\xa8\x5b\xd7\xdc\x74\x8c\xc8\x4f\x0a\x9b\x89\x3f\xe7\x2b\xbc\x16\x68\x95\x23\x86\xce\x67\x22\x49\x35\xad\xc1\xf7\x5f\xe3\x2a\x6b\xe4\x21\x26\x44\xc1\xa1\x5e\x9c\xa6\x12\x75\x08\x32\xad\xfb\xe2\x54\x02\xdd\x3a\xc8\x0c\x7c\x9e\x25\xe5\x1b\x3e\x62\xb8\x63\x6a\x26\x6d\x04\x7e\x26\x74\x61\xae\x21\x26\xfb\xa2\x3b\x76\x99\xeb\x0a\xb8\x2d\x22\xad\x2e\x77\xfd\xcd\x46\x8c\xa9\x59\x47\x71\x36\x73\x18\x9b\x39\xd7\x56\x08\x63\x91\xb0\x4a\xe3\x17\x08\x72\x26\x98\xc1\x6b\xe0\x89\x88\x12\x98\x99\x5d\x87\x2e\xdc\x64\xf7\xad\x05\x6f\x6f\x36\xbf\x9c\x4b\x0e\x0c\xf6\xc4\x8f\xe8\xcf\xff\xda\x00\x0c\x03\x01\x00\x02\x00\x03\x00\x00\x00\x10\x89\x4a\x5d\xe3\xb4\xf3\x74\xe6\x50\xee\x11\x27\xec\x65\x8f\xfc\x21\x26\x5f\xff\xc4\x00\x23\x11\x01\x00\x02\x02\x02\x02\x02\x03\x01\x01\x00\x00\x00\x00\x00\x00\x01\x11\x21\x00\x31\x41\x51\x61\x71\x81\x91\xa1\xb1\xc1\xd1\xf0\xff\xda\x00\x08\x01\x03\x01\x01\x3f\x10\x69\xa6\x38\x70\x4d\x4f\x85\x62\x78\xd6\x40\x85\xbf\x37\xff\x00\x7f\x98\x74\x08\x6d\x78\x9d\xa4\xd4\x9c\x71\x3f\x78\xb9\x0e\x8e\xdb\xda\xcd\xc9\xb8\x0f\x9c\x59\x47\x22\xce\x2b\x5b\x9e\xb9\xe6\xfc\x61\x66\x9d\x91\x11\x3a\x67\xc6\xb4\x78\xf3\x8b\x25\xfb\x1c\x04\xb8\xd8\x95\x60\xbf\x47\xcd\x6b\x13\xa0\x74\x44\x1a\x0a\xaa\x46\x4b\x32\x48\x69\xc7\x11\x1e\xa7\x7b\xf9\x91\xc1\x43\x4d\xf9\x9e\xfe\x71\xf1\x4b\x70\x7f\x7a\xc4\x00\xa0\xb2\x77\x32\xfe\x0f\x8f\x38\x0c\x26\x17\x9f\x39\x11\xc2\x33\x74\x22\x12\x6d\x99\xd1\xad\xae\x2d\x92\x28\x07\x6f\x07\x6b\xf2\xd7\x46\x18\x56\x89\x6d\x9e\xa5\xb8\xfd\x7a\xc1\x79\x02\x64\xb1\x31\xc8\xc1\x1d\x46\xfe\x63\x08\x5a\x57\x05\x5d\x84\xce\xef\x67\x67\xa3\xca\x0d\xd4\x24\xcc\x5d\xfa\xe9\x86\x9c\x2b\x91\x2e\xbb\xbc\x14\x35\x5d\x9a\x01\x2a\xbd\xc3\x9f\x9a\x9c\xa6\x10\x0c\x8b\x69\xb7\xd3\x06\xb7\xba\xee\x00\x9c\xd8\xd9\xd3\x14\x03\xef\xd3\x38\xdd\x42\xb2\x93\xc1\xc1\xd4\xee\x28\xfc\x61\xee\x70\x9e\xcf\x9c\xae\x44\xa4\x98\xb8\x5d\x9c\x4c\x4e\x98\xe7\x00\xd3\x9a\xde\xbe\xb0\xa2\x44\x04\x09\x69\x0b\x36\x6e\xe6\x65\x83\x58\x26\x9e\x64\xca\xdf\x2d\x57\x8f\xb9\x6f\x24\x6c\x04\xa2\x93\x84\xfd\xf8\xd4\x60\x9a\x8c\x38\x43\x14\x49\xe0\xc7\x60\x40\x02\x25\x0b\x71\xea\x12\x64\x24\xe6\xf0\xa4\x59\x61\x51\xc5\xf7\xf6\x79\x19\xc2\x03\x58\xc2\x81\x3b\x01\x38\xe4\x88\x3d\x44\x6f\x1b\x98\xc9\x92\x92\x7a\x3b\xf4\xd1\x8c\x8a\xc0\x2b\xf1\xee\xa7\x8f\xee\x18\xa2\x0d\x31\x76\x0c\x47\xf2\x69\x36\xe4\x0e\x88\x0d\x1d\x93\x5e\xa7\x7c\xc4\x39\x04\x91\x7f\xe7\x59\xb3\xe3\xf8\x61\x29\x23\x21\xdd\xf3\x84\x1a\x1c\x0a\x3f\x18\x88\x6d\x85\x3c\x9c\x56\x4a\xe9\xa3\x77\xd6\x10\x36\xe1\xfd\x64\xbf\xe8\xe7\xff\xc4\x00\x24\x11\x01\x01\x00\x02\x02\x02\x01\x04\x03\x01\x00\x00\x00\x00\x00\x00\x01\x11\x21\x31\x00\x41\x51\x61\x71\x81\x91\xb1\xc1\xa1\xd1\xf1\xe1\xff\xda\x00\x08\x01\x02\x01\x01\x3f\x10\xd7\xa7\x57\x3d\x84\x75\x41\x50\xae\x6b\xe7\x8e\x43\x43\xe5\x3b\xff\x00\x9f\x7e\x68\xbc\x16\x48\xbe\xe6\xaf\x67\x7d\x78\xe2\x89\x90\xa0\x53\x16\x3a\x80\x1b\x59\xc2\xdd\x06\x5b\x21\x0b\xa2\x02\xa1\x8c\xef\x1d\x6f\xcb\x98\x8a\x0b\x93\x8c\x3d\x9c\xb5\xdb\x0d\xda\xc2\x20\xe0\xc7\x03\x46\x8d\x02\xdc\x88\xef\x76\xbf\x4c\x57\x9e\xd8\x3c\xf5\x44\xbe\xa7\xfb\xc0\x0b\x48\x65\xf0\xfe\x19\x6c\x4c\x75\xc1\x70\xe0\x22\x42\x6e\x47\x53\xbf\x1d\xf8\x40\x99\xb1\x95\x73\x83\xcf\xdd\xc1\x98\x0e\x39\x17\xcc\x83\xa6\xbb\x9a\x67\xee\xf8\xe0\x44\x00\xeb\xfd\x73\x0a\x4d\xa9\x17\x2b\x69\xd7\xdf\x63\x21\x78\x88\xe4\x17\x1b\x2e\x3a\xa6\x97\xb3\x13\xbe\x04\x46\x2d\xe8\xf1\x83\x67\xae\xf1\xee\xa3\xb2\x0b\x64\xcf\xbf\xae\x2f\xc7\x1a\xb3\x19\x60\xe1\x73\x82\x4d\x1d\x84\xcf\x28\x89\x91\x03\x95\x14\xf8\x83\x77\xf7\xc8\x99\xea\x57\x33\x2e\x74\x10\xf8\x38\x62\x58\xe9\xeb\x52\x75\x8a\x43\xe3\x61\xc7\xce\xa2\x40\x58\xac\x2b\xe2\xc2\x98\xa9\x9a\x50\xbb\xca\xb0\x4c\x56\x9a\x8d\x77\x93\x4c\xa7\x10\x14\x6c\xec\xf6\x91\xd4\x4e\xf3\x31\xc6\x1d\xc2\xdd\xc2\x76\xa4\xea\x77\xae\xf8\xb7\xd2\x04\xc0\xa0\xa1\xe5\xb7\x5b\xc7\x7c\x70\x7f\x3e\x60\x54\xef\x48\xb9\xcd\x7d\xe6\x9e\xf5\xc3\x46\x51\x2b\xac\x1c\x86\x72\x4d\xfd\x08\x71\x1c\xed\x00\xe5\x62\xaa\xe7\xa3\xda\xa9\x3a\xe6\x63\xa2\xc9\x66\x6b\x0f\xe9\xf7\xcb\x80\x6b\x83\x80\x98\x3a\xb1\x82\x36\x9a\x67\x14\xa4\x25\x13\x2e\x58\xe0\xd0\xd6\x2f\x56\x50\xa2\xd3\xee\x1f\xd7\x03\x71\xcc\x66\x78\x1a\xec\xcc\x62\xf5\x71\xc9\x6f\x24\x75\x07\x66\x8c\x7e\x3e\xb7\x84\x88\xc3\x7c\xa9\x02\x47\x02\xb7\xe4\x9b\x78\x02\xe1\x11\x2f\x4a\xfe\x9d\x7d\x39\x8c\x7b\xef\x60\xb1\xbd\x4e\xf1\x1b\xa3\x7c\xa8\xa9\x24\x14\xab\x37\x8c\x46\x44\x42\xa6\x6b\xcb\xd4\xce\x3c\x75\x2b\xf9\x6b\xef\xc4\x0a\x28\x1e\xcc\x3a\xf1\xc5\x67\x46\x15\x59\x72\x97\x5c\x96\xe4\xb2\x56\x4f\x1c\x39\x56\x31\x97\x19\x9f\x8c\x71\xa9\xaf\xf9\xc3\x98\x20\xe7\xff\xc4\x00\x1f\x10\x01\x01\x01\x01\x01\x01\x00\x02\x03\x01\x00\x00\x00\x00\x00\x00\x01\x11\x21\x00\x31\x41\x51\x61\x71\x81\xd1\x91\xff\xda\x00\x08\x01\x01\x00\x01\x3f\x10\xca\x95\xf8\x23\xa1\xff\x00\xa4\x51\xaa\x3a\xfa\xba\x8a\x07\x12\x09\xa8\x42\x89\x66\x0b\x78\x98\x94\xbb\x7a\x81\x41\xf3\x68\x54\xa4\x3f\x3f\x99\xc1\xac\x14\x9f\xe5\xc9\xa2\x63\xd4\x20\x1b\x60\x03\x7e\x78\x61\xc5\x9d\xc9\x6c\xce\x48\x04\x84\xb7\x5f\x9a\x7d\x70\x8f\xbd\x3f\xd3\x67\x03\x79\x27\x33\x40\x32\x80\x81\x2e\x33\xcb\xb5\xa4\xcd\x43\x4a\x92\x3e\x10\x97\xe1\xaa\xf3\x20\x83\x66\x0b\x16\x9a\x80\x3d\xec\x0c\x91\xb3\x44\xe8\x5b\x0b\x8d\x1f\x15\xe0\x07\xc3\x51\x49\x28\x68\x37\x84\xfc\xfd\x79\x70\x08\x93\xb5\x3c\x31\x4b\xf5\x4c\xf5\xe7\x5a\x4a\x54\x8a\x27\xc4\x7e\x72\x15\xef\x43\x85\x70\x3f\x25\x2a\x81\x64\x77\x7f\xa5\x88\xd3\x25\xd2\x8a\x07\x81\xcf\xe8\x65\xa6\xd2\x16\x0a\x8e\x3f\x5f\x97\x84\xd5\x81\x42\x90\x9f\x1c\xac\x10\x3c\x87\x7a\x81\xe8\x2e\x58\x00\x97\xc1\xbb\x76\xf0\x0e\xf9\x41\x10\x80\x53\x24\x8d\xf8\x8f\x36\x34\x8d\x9c\x21\x6b\x6e\x7d\xde\x42\x2f\x58\x58\x0d\x08\x88\x44\x6b\xc5\x38\x44\x28\x0a\x56\xb5\x1d\xdb\x2a\xe0\x71\x40\xe6\xd7\x40\x8d\xc4\xa0\x02\xe2\x8a\xa0\xf9\x1b\x8f\x96\x33\x41\x0a\x26\xbc\x2a\xfb\x8e\x25\x08\xf0\xbf\xb3\xed\xca\x89\x8f\x41\xa8\x18\xda\x07\xd8\x1a\x88\xe6\x60\x11\x2f\xd3\x5b\x9f\xbd\xe6\x98\x60\x8c\xc5\x3e\x18\x03\x63\x3d\x87\x04\x50\x82\x8a\xc5\x70\x53\x01\x29\x9e\x1d\xec\xd3\x64\x68\xc0\x80\x91\x5f\xc0\xe0\x04\xab\x86\x18\x84\x04\x92\xfa\x1a\x8f\xa4\xe9\xbb\x42\xa6\xa8\xfe\xd0\x1a\x15\x21\x73\x6e\xd0\x53\x3c\x60\x00\x28\x86\x5a\xf3\x95\xfb\x8c\x82\xf8\x33\xce\x7e\xee\x61\xdf\x91\x84\x0f\x45\xf4\xbe\x5e\x97\xcc\x8b\x06\xca\xc1\x34\xa5\x65\xe9\x16\xd6\x05\xda\x00\xc4\x11\x49\x27\x0c\x3a\xaa\x84\x8a\x5a\x11\x70\x81\xf5\x84\xe5\x4c\x1d\x0d\x86\x1e\x12\x8b\x82\x3e\x1c\xe9\xe7\x22\x03\x5b\x0e\x59\x08\x31\x4e\x73\x89\x22\x5d\x2d\x66\xdf\xc7\x01\x35\x22\x85\x04\x7d\x13\x27\xe3\x88\x8b\x69\x72\x89\x62\x7e\xb8\xcf\x34\xdf\x40\x00\xa0\x28\x5f\x05\x38\x49\x00\x4b\xf4\xa5\xc5\xd5\x7f\x95\x7a\x26\x52\x46\x28\x4b\x69\x1b\xf5\x3f\x79\xdc\x04\x5a\x04\x8e\x7e\xb9\xad\x75\x05\x5f\xf6\xef\xff\xd9", expectedMatch: /Render_Image\('Raw'\)/, recipeConfig: [ { diff --git a/tests/operations/tests/Media.mjs b/tests/operations/tests/Media.mjs index 96116167..cbf09c5c 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/MorseCode.mjs b/tests/operations/tests/MorseCode.mjs index 1da2cb78..8df76d80 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/MultipleBombe.mjs b/tests/operations/tests/MultipleBombe.mjs index 32d2db08..adec6174 100644 --- a/tests/operations/tests/MultipleBombe.mjs +++ b/tests/operations/tests/MultipleBombe.mjs @@ -4,7 +4,7 @@ * @copyright Crown Copyright 2019 * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/NetBIOS.mjs b/tests/operations/tests/NetBIOS.mjs index f7210196..923853ea 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/OTP.mjs b/tests/operations/tests/OTP.mjs index a9198798..c34a46f2 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/PGP.mjs b/tests/operations/tests/PGP.mjs index baf76fb8..18e2e7ed 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; const ASCII_TEXT = "A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools."; @@ -248,7 +248,8 @@ IOE1W/Zqmqzq+4frwnzWwYv9/U1RwIs/qlFVnzliREOzW+om8EncSSd7fQ== =fEAT -----END PGP MESSAGE----- `, - expectedOutput: `Signed by PGP fingerprint: e94e06dd0b3744a0e970de9d84246548df98e485 + expectedOutput: `Signed by PGP key ID: DF98E485 +PGP fingerprint: e94e06dd0b3744a0e970de9d84246548df98e485 Signed on Tue, 29 May 2018 15:44:52 GMT ---------------------------------- ${UTF8_TEXT}`, @@ -282,4 +283,30 @@ H2qMY1O7hezH3fp+EZzCAccJMtK7VPk13WAgMRH22HirG4aK1i75IVOtjBgObzDh } ] }, + { + name: "PGP Verify: ASCII, Alice", + input: `-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools. +-----BEGIN PGP SIGNATURE----- + +iLMEAQEIAB0WIQRLbJy6MLpYOr9qojE+2VNAUiMLOgUCXRTsvwAKCRA+2VNAUiML +OuaHBADMMNtsuN92Fb+UrDimsv6TDQpbJhDkwp9kZdKYP5HAmSYAhXBG7N+YCMw+ +v2FSpUu9jJiPBm1K1SEwLufQVexoRv6RsBNolRFB07sArau0s0DnIXUchCZWvyTP +1KsjBnDr84U2b11H58g4DlTT4gQrz30rFuHz9AGmPAtDHbSXIA== +=vnk/ +-----END PGP SIGNATURE-----`, + expectedOutput: `Signed by PGP key ID: DF98E485 +PGP fingerprint: e94e06dd0b3744a0e970de9d84246548df98e485 +Signed on Thu, 27 Jun 2019 16:20:15 GMT +---------------------------------- +A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools.`, + recipeConfig: [ + { + "op": "PGP Verify", + "args": [ALICE_PUBLIC] + } + ] + } ]); diff --git a/tests/operations/tests/PHP.mjs b/tests/operations/tests/PHP.mjs index dabd4d39..b9d6a8f0 100644 --- a/tests/operations/tests/PHP.mjs +++ b/tests/operations/tests/PHP.mjs @@ -7,7 +7,7 @@ * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/ParseIPRange.mjs b/tests/operations/tests/ParseIPRange.mjs index 83a8eea9..d7ef0b52 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/ParseQRCode.mjs b/tests/operations/tests/ParseQRCode.mjs index 191699d4..dbccabd3 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/ParseTLV.mjs b/tests/operations/tests/ParseTLV.mjs index 5617bf2b..5c99eee2 100644 --- a/tests/operations/tests/ParseTLV.mjs +++ b/tests/operations/tests/ParseTLV.mjs @@ -6,7 +6,7 @@ * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/PowerSet.mjs b/tests/operations/tests/PowerSet.mjs index c742b030..c7efca84 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Protobuf.mjs b/tests/operations/tests/Protobuf.mjs index 957e4d54..0bdd6b19 100644 --- a/tests/operations/tests/Protobuf.mjs +++ b/tests/operations/tests/Protobuf.mjs @@ -6,7 +6,7 @@ * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Regex.mjs b/tests/operations/tests/Regex.mjs index b48673dc..a8697d46 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Register.mjs b/tests/operations/tests/Register.mjs index dec017dd..7c02ae0b 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/RemoveDiacritics.mjs b/tests/operations/tests/RemoveDiacritics.mjs index 1b712ce3..c58a2ba6 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Rotate.mjs b/tests/operations/tests/Rotate.mjs index 83e39ee8..fe832b4a 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ diff --git a/tests/operations/tests/SeqUtils.mjs b/tests/operations/tests/SeqUtils.mjs index 6947928d..3076cf96 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/SetDifference.mjs b/tests/operations/tests/SetDifference.mjs index 1990d19d..40fac524 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/SetIntersection.mjs b/tests/operations/tests/SetIntersection.mjs index 72d2529b..c9146c01 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/SetUnion.mjs b/tests/operations/tests/SetUnion.mjs index 1593d202..f6517118 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/SplitColourChannels.mjs b/tests/operations/tests/SplitColourChannels.mjs index 60788425..4452d970 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; // Base 85 encoded const testCard = "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"; const testCardSplit = "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"; diff --git a/tests/operations/tests/StrUtils.mjs b/tests/operations/tests/StrUtils.mjs index 515b5005..9098d2f1 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/SymmetricDifference.mjs b/tests/operations/tests/SymmetricDifference.mjs index d99783a3..8e589c1b 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/TextEncodingBruteForce.mjs b/tests/operations/tests/TextEncodingBruteForce.mjs index 74408576..dfc5073b 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/ToFromInsensitiveRegex.mjs b/tests/operations/tests/ToFromInsensitiveRegex.mjs index fa191951..b74e9973 100644 --- a/tests/operations/tests/ToFromInsensitiveRegex.mjs +++ b/tests/operations/tests/ToFromInsensitiveRegex.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2018 * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/ToGeohash.mjs b/tests/operations/tests/ToGeohash.mjs new file mode 100644 index 00000000..96dece85 --- /dev/null +++ b/tests/operations/tests/ToGeohash.mjs @@ -0,0 +1,55 @@ +/** + * To Geohash tests + * + * @author gchq77703 + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +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], + }, + ], + }, +]); diff --git a/tests/operations/tests/TranslateDateTimeFormat.mjs b/tests/operations/tests/TranslateDateTimeFormat.mjs index a60459fd..571fe0e5 100644 --- a/tests/operations/tests/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 "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/Typex.mjs b/tests/operations/tests/Typex.mjs index e3751e8a..f7f3d757 100644 --- a/tests/operations/tests/Typex.mjs +++ b/tests/operations/tests/Typex.mjs @@ -4,7 +4,7 @@ * @copyright Crown Copyright 2019 * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/tests/operations/tests/YARA.mjs b/tests/operations/tests/YARA.mjs index e3c28ef1..267af2ef 100644 --- a/tests/operations/tests/YARA.mjs +++ b/tests/operations/tests/YARA.mjs @@ -6,7 +6,7 @@ * @copyright Crown Copyright 2019 * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { diff --git a/webpack.config.js b/webpack.config.js index effc70ba..46878282 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -53,14 +53,14 @@ module.exports = { ], resolve: { alias: { - jquery: "jquery/src/jquery" - } + jquery: "jquery/src/jquery", + }, }, module: { rules: [ { test: /\.m?js$/, - exclude: /node_modules\/(?!jsesc|crypto-api)/, + exclude: /node_modules\/(?!jsesc|crypto-api|bootstrap)/, options: { configFile: path.resolve(__dirname, "babel.config.js"), cacheDirectory: true, @@ -119,9 +119,17 @@ module.exports = { encoding: "base64" } }, + { // Store font .fnt and .png files in a separate fonts folder + test: /(\.fnt$|bmfonts\/.+\.png$)/, + loader: "file-loader", + options: { + name: "[name].[ext]", + outputPath: "assets/fonts" + } + }, { // First party images are saved as files to be cached test: /\.(png|jpg|gif)$/, - exclude: /node_modules/, + exclude: /(node_modules|bmfonts)/, loader: "file-loader", options: { name: "images/[name].[ext]"