diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..aca273a --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,39 @@ +module.exports = { + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "overrides": [ + { + "env": { + "node": true + }, + "files": [ + ".eslintrc.{js,cjs}" + ], + "parserOptions": { + "sourceType": "script" + } + } + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + // to enforce using type for object type definitions, can be type or interface + "@typescript-eslint/consistent-type-definitions": ["error", "interface"], + // force === and !== instead of == and != + "eqeqeq": ["error", "always"], + "prefer-const": ["error", {"destructuring" : "all"}] + } +} diff --git a/README.md b/README.md index 55a331e..dae6a4e 100644 --- a/README.md +++ b/README.md @@ -146,4 +146,4 @@ This line should come before you add anything else and will do the same thing as ## To Build Yourself -Clone the repo, make sure you have nodejs, then `npm run build` . This will generate the 'siunitx.js' file that's already in the root folder. +Clone the repo, make sure you have nodejs, then `npm run build` . This will generate the 'siunitx.js' file that's already in the root folder. \ No newline at end of file diff --git a/index.html b/index.html index ef7510e..50a5d9e 100644 --- a/index.html +++ b/index.html @@ -12,9 +12,11 @@ //color is required for color options //cancel is required for cancelation of units inlineMath: { '[+]': [['$', '$']] }, - /* global options can go here, see IOptions in options.ts for properties */ + /* global options for siunitx can go in the siunitx object on tex, see IOptions in options.ts for properties */ // Uncomment example below: - //perMode: 'fraction' + // siunitx: { + // perMode: 'fraction' + // } }, options:{ enableEnrichment:true, @@ -32,10 +34,14 @@ const mml = await MathJax.tex2mmlPromise(arr[capt], {}); var regex = /(?:data-semantic-speech=")(.*?)(?:")/g; var matches = [...mml.matchAll(regex)]; + if (matches[0] === undefined) { + break; // accessibility is not turned on yet + } table.rows[capt + 1].cells[2].innerText = matches[0][1]; } } + await fillTableWithSpeech('angTable', ang); await fillTableWithSpeech('numTable', num); await fillTableWithSpeech('complexnumTable', complexnum); @@ -118,8 +124,10 @@

DeclareSIUnit

var num = [ String.raw`\num{0}`, String.raw`\num{1234}`, + String.raw`\num{1234p}`, String.raw`\num{12345}`, String.raw`\num{0.123}`, + String.raw`\num{0.10000}`, String.raw`\num{.123}`, String.raw`\num{0,1234}`, String.raw`\num{.12345}`, @@ -286,14 +294,17 @@

DeclareSIUnit

String.raw`\num[uncertainty-mode = separate]{123.45(120)}`, String.raw`\num[uncertainty-mode = separate]{0.035(14)}`, String.raw`\num[uncertainty-mode=separate]{1.234 +- 0.005}`, - String.raw`\num[{uncertainty-mode=compact,output-open-uncertainty=[,output-close-uncertainty=],uncertainty-separator=\,}]{1.234(5)}`, + String.raw`\num[uncertainty-mode=compact, output-open-uncertainty=[, output-close-uncertainty=], uncertainty-separator=\,]{1.234(5)}`, String.raw`\num{-15673}`, String.raw`\num[bracket-negative-numbers]{-15673}`, String.raw`\num{-15673}`, String.raw`\num[negative-color = red]{-15673}`, + String.raw`\num[negative-color = red]{15673}`, String.raw`\num[negative-color = red,bracket-negative-numbers]{-15673}`, + String.raw`\num[negative-color = red,bracket-negative-numbers]{15673}`, + String.raw`\num{2e3}`, String.raw`\num[tight-spacing = true]{2e3}`, //TODO: Probably more places to add tight spacing, +- uncertainty probably @@ -302,14 +313,25 @@

DeclareSIUnit

String.raw`\num[print-implicit-plus]{345}`, String.raw`\num{1e4}`, String.raw`\num[print-unity-mantissa = false]{1e4}`, + String.raw`\num[print-unity-mantissa = false]{1.1e4}`, + String.raw`\num[print-unity-mantissa = false]{2e4}`, String.raw`\num{444e0}`, String.raw`\num[print-zero-exponent = true]{444e0}`, + String.raw`\num[print-unity-mantissa = true, print-zero-exponent = true]{1e0}`, + String.raw`\num[print-unity-mantissa = true, print-zero-exponent = false]{1e0}`, + String.raw`\num[print-unity-mantissa = false, print-zero-exponent = true]{1e0}`, + String.raw`\num[print-unity-mantissa = false, print-zero-exponent = false]{1e0}`, String.raw`\num{0.123}`, String.raw`\num[print-zero-integer = false]{0.123}`, + String.raw`\num{00.123}`, + String.raw`\num[print-zero-integer = false]{00.123}`, String.raw`\num{123.00}`, String.raw`\num[zero-decimal-as-symbol]{123.00}`, String.raw`\num[zero-decimal-as-symbol, zero-symbol=\text{[---]}]{123.00}`, + String.raw`\num[negative-color = red,bracket-negative-numbers,print-implicit-plus]{15673}`, + String.raw`\num[negative-color = red,bracket-negative-numbers,print-implicit-plus]{-15673}`, + ]; @@ -364,9 +386,10 @@

DeclareSIUnit

String.raw`\ang[angle-mode = decimal]{2.67}`, String.raw`\ang[angle-mode = decimal]{2;3;4}`, String.raw`\ang{2.67}`, - String.raw`\ang[number-angle-product = \,]{2.67}`, + String.raw`\ang[number-angle-product = {\,}]{2.67}`, String.raw`\ang{6;7;6.5}`, - String.raw`\ang[angle-separator = \,]{6;7;6.5}`, + String.raw`\ang[angle-separator = {\,}]{6;7;6.5}`, + String.raw`\ang[angle-separator = {\,}]{6;;}`, String.raw`\displaylines{ \ang{-1;;} \\ \ang{;-2;} \\ @@ -530,7 +553,7 @@

DeclareSIUnit

String.raw`\qty[per-mode = fraction]{1,345}{\coulomb\per\mole}`, // quantity-product test String.raw`\qty{2.67}{\farad}`, - String.raw`\qty[quantity-product = \ ]{2.67}{\farad}`, + String.raw`\qty[quantity-product = {\ }]{2.67}{\farad}`, String.raw`\qty[quantity-product = ]{2.67}{\farad}`, // prefix-mode, extract-mass-in-kilograms test String.raw`\qty{1e3}{\metre\second}`, diff --git a/package-lock.json b/package-lock.json index 1303973..630e551 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,8 +18,8 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.17.12", - "@typescript-eslint/eslint-plugin": "^5.30.5", - "@typescript-eslint/parser": "^5.30.5", + "@typescript-eslint/eslint-plugin": "^6.17.0", + "@typescript-eslint/parser": "^6.17.0", "eslint": "^8.19.0", "ts-loader": "^9.3.0", "typescript": "^4.7.4", @@ -1568,6 +1568,30 @@ "node": ">=10.0.0" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", @@ -1764,41 +1788,49 @@ "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "node_modules/@types/node": { "version": "17.0.41", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz", "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==" }, + "node_modules/@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.5.tgz", - "integrity": "sha512-lftkqRoBvc28VFXEoRgyZuztyVUQ04JvUnATSPtIRFAccbXTWL6DEtXGYMcbg998kXw1NLUJm7rTQ9eUt+q6Ig==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.17.0.tgz", + "integrity": "sha512-Vih/4xLXmY7V490dGwBQJTpIZxH4ZFH6eCVmQ4RFkB+wmaCTDAx4dtgoWwMNGKLkqRY1L6rPqzEbjorRnDo4rQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.30.5", - "@typescript-eslint/type-utils": "5.30.5", - "@typescript-eslint/utils": "5.30.5", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.17.0", + "@typescript-eslint/type-utils": "6.17.0", + "@typescript-eslint/utils": "6.17.0", + "@typescript-eslint/visitor-keys": "6.17.0", "debug": "^4.3.4", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.2.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1807,9 +1839,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1821,41 +1853,27 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, "node_modules/@typescript-eslint/parser": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.5.tgz", - "integrity": "sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.17.0.tgz", + "integrity": "sha512-C4bBaX2orvhK+LlwrY8oWGmSl4WolCfYm513gEccdWZj0CwGadbIADb0FtVEcI+WzUyjyoBj2JRP8g25E6IB8A==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.30.5", - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/typescript-estree": "5.30.5", + "@typescript-eslint/scope-manager": "6.17.0", + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/typescript-estree": "6.17.0", + "@typescript-eslint/visitor-keys": "6.17.0", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1864,16 +1882,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.5.tgz", - "integrity": "sha512-NJ6F+YHHFT/30isRe2UTmIGGAiXKckCyMnIV58cE3JkHmaD6e5zyEYm5hBDv0Wbin+IC0T1FWJpD3YqHUG/Ydg==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.17.0.tgz", + "integrity": "sha512-RX7a8lwgOi7am0k17NUO0+ZmMOX4PpjLtLRgLmT1d3lBYdWH4ssBUbwdmc5pdRX8rXon8v9x8vaoOSpkHfcXGA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/visitor-keys": "5.30.5" + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/visitor-keys": "6.17.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1881,24 +1899,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.5.tgz", - "integrity": "sha512-k9+ejlv1GgwN1nN7XjVtyCgE0BTzhzT1YsQF0rv4Vfj2U9xnslBgMYYvcEYAFVdvhuEscELJsB7lDkN7WusErw==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.17.0.tgz", + "integrity": "sha512-hDXcWmnbtn4P2B37ka3nil3yi3VCQO2QEB9gBiHJmQp5wmyQWqnjA85+ZcE8c4FqnaB6lBwMrPkgd4aBYz3iNg==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.30.5", + "@typescript-eslint/typescript-estree": "6.17.0", + "@typescript-eslint/utils": "6.17.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1906,28 +1925,13 @@ } } }, - "node_modules/@typescript-eslint/type-utils/node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, "node_modules/@typescript-eslint/types": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.5.tgz", - "integrity": "sha512-kZ80w/M2AvsbRvOr3PjaNh6qEW1LFqs2pLdo2s5R38B2HYXG8Z0PP48/4+j1QHJFL3ssHIbJ4odPRS8PlHrFfw==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.17.0.tgz", + "integrity": "sha512-qRKs9tvc3a4RBcL/9PXtKSehI/q8wuU9xYJxe97WFxnzH8NWWtcW3ffNS+EWg8uPvIerhjsEZ+rHtDqOCiH57A==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1935,21 +1939,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.5.tgz", - "integrity": "sha512-qGTc7QZC801kbYjAr4AgdOfnokpwStqyhSbiQvqGBLixniAKyH+ib2qXIVo4P9NgGzwyfD9I0nlJN7D91E1VpQ==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.17.0.tgz", + "integrity": "sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/visitor-keys": "5.30.5", + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/visitor-keys": "6.17.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1961,10 +1966,34 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1976,56 +2005,57 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "node_modules/@typescript-eslint/utils": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz", + "integrity": "sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==", "dev": true, "dependencies": { - "tslib": "^1.8.1" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.17.0", + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/typescript-estree": "6.17.0", + "semver": "^7.5.4" }, "engines": { - "node": ">= 6" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + "eslint": "^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.5.tgz", - "integrity": "sha512-o4SSUH9IkuA7AYIfAvatldovurqTAHrfzPApOZvdUq01hHojZojCFXx06D/aFpKCgWbMPRdJBWAC3sWp3itwTA==", + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.30.5", - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/typescript-estree": "5.30.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "lru-cache": "^6.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "bin": { + "semver": "bin/semver.js" }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "engines": { + "node": ">=10" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.5.tgz", - "integrity": "sha512-D+xtGo9HUMELzWIUqcQc0p2PO4NyvTrgIOK/VnSH083+8sq0tiLozNRKuLarwHYGRuA6TVBQSuuLwJUDWd3aaA==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.17.0.tgz", + "integrity": "sha512-H6VwB/k3IuIeQOyYczyyKN8wH6ed8EwliaYHLxOIhyF0dYEIsN8+Bk3GE19qafeMKyZJJHP8+O1HiFhFLUNKSg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.30.5", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.17.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -3079,12 +3109,15 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/ansi-styles": { @@ -3338,9 +3371,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -3396,9 +3429,9 @@ "dev": true }, "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -3594,6 +3627,12 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -3644,9 +3683,9 @@ } }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "dev": true, "engines": { "node": ">= 4" @@ -5038,6 +5077,18 @@ "node": ">=8.0" } }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-loader": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.3.0.tgz", @@ -5142,12 +5193,6 @@ "node": ">=8" } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6560,6 +6605,21 @@ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true + }, "@eslint/eslintrc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", @@ -6724,159 +6784,171 @@ "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "@types/node": { "version": "17.0.41", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz", "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==" }, + "@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.5.tgz", - "integrity": "sha512-lftkqRoBvc28VFXEoRgyZuztyVUQ04JvUnATSPtIRFAccbXTWL6DEtXGYMcbg998kXw1NLUJm7rTQ9eUt+q6Ig==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.17.0.tgz", + "integrity": "sha512-Vih/4xLXmY7V490dGwBQJTpIZxH4ZFH6eCVmQ4RFkB+wmaCTDAx4dtgoWwMNGKLkqRY1L6rPqzEbjorRnDo4rQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.30.5", - "@typescript-eslint/type-utils": "5.30.5", - "@typescript-eslint/utils": "5.30.5", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.17.0", + "@typescript-eslint/type-utils": "6.17.0", + "@typescript-eslint/utils": "6.17.0", + "@typescript-eslint/visitor-keys": "6.17.0", "debug": "^4.3.4", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.2.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "dependencies": { "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" } - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } } } }, "@typescript-eslint/parser": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.5.tgz", - "integrity": "sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.17.0.tgz", + "integrity": "sha512-C4bBaX2orvhK+LlwrY8oWGmSl4WolCfYm513gEccdWZj0CwGadbIADb0FtVEcI+WzUyjyoBj2JRP8g25E6IB8A==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.30.5", - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/typescript-estree": "5.30.5", + "@typescript-eslint/scope-manager": "6.17.0", + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/typescript-estree": "6.17.0", + "@typescript-eslint/visitor-keys": "6.17.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.5.tgz", - "integrity": "sha512-NJ6F+YHHFT/30isRe2UTmIGGAiXKckCyMnIV58cE3JkHmaD6e5zyEYm5hBDv0Wbin+IC0T1FWJpD3YqHUG/Ydg==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.17.0.tgz", + "integrity": "sha512-RX7a8lwgOi7am0k17NUO0+ZmMOX4PpjLtLRgLmT1d3lBYdWH4ssBUbwdmc5pdRX8rXon8v9x8vaoOSpkHfcXGA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/visitor-keys": "5.30.5" + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/visitor-keys": "6.17.0" } }, "@typescript-eslint/type-utils": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.5.tgz", - "integrity": "sha512-k9+ejlv1GgwN1nN7XjVtyCgE0BTzhzT1YsQF0rv4Vfj2U9xnslBgMYYvcEYAFVdvhuEscELJsB7lDkN7WusErw==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.17.0.tgz", + "integrity": "sha512-hDXcWmnbtn4P2B37ka3nil3yi3VCQO2QEB9gBiHJmQp5wmyQWqnjA85+ZcE8c4FqnaB6lBwMrPkgd4aBYz3iNg==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.30.5", + "@typescript-eslint/typescript-estree": "6.17.0", + "@typescript-eslint/utils": "6.17.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "dependencies": { - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - } + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.5.tgz", - "integrity": "sha512-kZ80w/M2AvsbRvOr3PjaNh6qEW1LFqs2pLdo2s5R38B2HYXG8Z0PP48/4+j1QHJFL3ssHIbJ4odPRS8PlHrFfw==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.17.0.tgz", + "integrity": "sha512-qRKs9tvc3a4RBcL/9PXtKSehI/q8wuU9xYJxe97WFxnzH8NWWtcW3ffNS+EWg8uPvIerhjsEZ+rHtDqOCiH57A==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.5.tgz", - "integrity": "sha512-qGTc7QZC801kbYjAr4AgdOfnokpwStqyhSbiQvqGBLixniAKyH+ib2qXIVo4P9NgGzwyfD9I0nlJN7D91E1VpQ==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.17.0.tgz", + "integrity": "sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/visitor-keys": "5.30.5", + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/visitor-keys": "6.17.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "dependencies": { - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { - "lru-cache": "^6.0.0" + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" } }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { - "tslib": "^1.8.1" + "lru-cache": "^6.0.0" } } } }, "@typescript-eslint/utils": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.5.tgz", - "integrity": "sha512-o4SSUH9IkuA7AYIfAvatldovurqTAHrfzPApOZvdUq01hHojZojCFXx06D/aFpKCgWbMPRdJBWAC3sWp3itwTA==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz", + "integrity": "sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==", "dev": true, "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.30.5", - "@typescript-eslint/types": "5.30.5", - "@typescript-eslint/typescript-estree": "5.30.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.17.0", + "@typescript-eslint/types": "6.17.0", + "@typescript-eslint/typescript-estree": "6.17.0", + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "@typescript-eslint/visitor-keys": { - "version": "5.30.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.5.tgz", - "integrity": "sha512-D+xtGo9HUMELzWIUqcQc0p2PO4NyvTrgIOK/VnSH083+8sq0tiLozNRKuLarwHYGRuA6TVBQSuuLwJUDWd3aaA==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.17.0.tgz", + "integrity": "sha512-H6VwB/k3IuIeQOyYczyyKN8wH6ed8EwliaYHLxOIhyF0dYEIsN8+Bk3GE19qafeMKyZJJHP8+O1HiFhFLUNKSg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.30.5", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.17.0", + "eslint-visitor-keys": "^3.4.1" } }, "@webassemblyjs/ast": { @@ -7761,9 +7833,9 @@ } }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "esm": { @@ -7851,9 +7923,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -7907,9 +7979,9 @@ "dev": true }, "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -8057,6 +8129,12 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -8089,9 +8167,9 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "dev": true }, "import-fresh": { @@ -9073,6 +9151,13 @@ "is-number": "^7.0.0" } }, + "ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "requires": {} + }, "ts-loader": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.3.0.tgz", @@ -9145,12 +9230,6 @@ } } }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 2994e0f..0a7853b 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "main": "./js/siunitx.js", "devDependencies": { "@babel/preset-typescript": "^7.17.12", - "@typescript-eslint/eslint-plugin": "^5.30.5", - "@typescript-eslint/parser": "^5.30.5", + "@typescript-eslint/eslint-plugin": "^6.17.0", + "@typescript-eslint/parser": "^6.17.0", "eslint": "^8.19.0", "ts-loader": "^9.3.0", "typescript": "^4.7.4", @@ -21,9 +21,10 @@ "webpack-cli": "^4.9.2" }, "scripts": { - "build": "tsc && node ./node_modules/mathjax-full/components/bin/makeAll", + "build": "tsc && node ./node_modules/mathjax-full/components/bin/makeAll --no-subdirs", "release": "standard-version", - "test": "echo 'No tests yet'" + "test": "echo 'No tests yet'", + "lint": "eslint --ext .js,.ts" }, "author": "", "license": "ISC", diff --git a/siunitx.js b/siunitx.js index d313636..f7e858d 100644 --- a/siunitx.js +++ b/siunitx.js @@ -1 +1 @@ -(function(){"use strict";var __webpack_modules__={100:function(e,n,t){t.d(n,{W:function(){return a}});var r=t(516);function i(e,n){for(var t=0;t0;)n=n.childNodes[0];return n.isToken?a.Z.getText(n):""}function l(e,n,t,a,l,u,c){var p=o[a.trimStart()];void 0===p&&(p=s(new r.Z(a,i.ZE.stack.env,i.ZE.configuration).mml()));if(e.length>=t){var _=e.split(""),m=0,f=-1!=u&&null!=u?u:n,g=f;if(l)for(var d=f;d<_.length;d+=g)e=e.slice(0,d+m)+p+e.slice(d+m,e.length+m),m+=p.length,g=-1!=c&&null!=c?c:n;else for(var h=_.length-f;h>=0;h-=g)e=e.slice(0,h)+p+e.slice(h,e.length+m),m+=p.length,g=-1!=c&&null!=c?c:n}return e}var u=new Map([["all",function(e,n){e.whole=l(e.whole,n.digitGroupSize,n.groupMinimumDigits,n.groupSeparator,!1,n.digitGroupFirstSize,n.digitGroupOtherSize),e.fractional=l(e.fractional,n.digitGroupSize,n.groupMinimumDigits,n.groupSeparator,!0,n.digitGroupFirstSize,n.digitGroupOtherSize)}],["decimal",function(e,n){e.fractional=l(e.fractional,n.digitGroupSize,n.groupMinimumDigits,n.groupSeparator,!0,n.digitGroupFirstSize,n.digitGroupOtherSize)}],["integer",function(e,n){e.whole=l(e.whole,n.digitGroupSize,n.groupMinimumDigits,n.groupSeparator,!1,n.digitGroupFirstSize,n.digitGroupOtherSize)}],["none",function(){}]]);function c(e,n,t){if("pm"!=e.type&&""==e.decimal){var r=n.fractional.length-e.whole.length;if(r>=0){for(var i=0;i0?(e.fractional=e.whole.slice(r,e.whole.length),e.whole=e.whole.slice(0,r),""!==e.fractional&&(e.decimal=t.outputDecimalMarker)):r<0&&"full"===t.uncertaintyMode&&(e.fractional="".padEnd(Math.abs(r),"0")+e.whole,e.whole="0",e.decimal=t.outputDecimalMarker)}}else if(+(e.whole+e.decimal+e.fractional)<1){for(var i=0,a=0;a0?n.uncertainty[n.uncertainty.length-1]:n).exponentMarker?t.exponent+=e:""!=t.decimal?t.fractional+=e:t.whole+=e}function parseDecimals(e,n){(n.uncertainty.length>0?n.uncertainty[n.uncertainty.length-1]:n).decimal+=e}function parseComparators(e,n){(n.uncertainty.length>0?n.uncertainty[n.uncertainty.length-1]:n).prefix+=e}function parseExponentMarkers(e,n){n.exponentMarker+=e}function parseSigns(e,n){var t;""!=(t=n.uncertainty.length>0?n.uncertainty[n.uncertainty.length-1]:n).exponentMarker?t.exponentSign+=e:t.sign+=e}function parseOpenUncertainty(e,n){var t=Object.assign(Object.assign({},UncertaintyDefault),{type:"bracket"});n.uncertainty.push(t)}function parseCloseUncertainty(e,n){if(0==n.uncertainty.length)throw new mathjax_full_js_input_tex_TexError__WEBPACK_IMPORTED_MODULE_0__.Z("50","No uncertainty parsed to close.");var t=n.uncertainty[n.uncertainty.length-1];if(t.completed)throw new mathjax_full_js_input_tex_TexError__WEBPACK_IMPORTED_MODULE_0__.Z("51","Uncertainty was already closed.");t.completed=!0}function parseUncertaintySigns(e,n){var t=Object.assign(Object.assign({},UncertaintyDefault),{type:"pm"});n.uncertainty.push(t)}function parseIgnore(){}function generateNumberMapping(e){for(var n,t=new Map,r=/[^\\\s]|(?:\\[^\\]*(?=\s|\\|$))/g;null!==(n=r.exec(e.inputComparators));)t.set(n[0],parseComparators);for(;null!==(n=r.exec(e.inputSigns));)t.set(n[0],parseSigns);for(;null!==(n=r.exec(e.inputDigits));)t.set(n[0],parseDigits);for(;null!==(n=r.exec(e.inputDecimalMarkers));)t.set(n[0],parseDecimals);for(;null!==(n=r.exec(e.inputOpenUncertainty));)t.set(n[0],parseOpenUncertainty);for(;null!==(n=r.exec(e.inputCloseUncertainty));)t.set(n[0],parseCloseUncertainty);for(;null!==(n=r.exec(e.inputUncertaintySigns));)if(t.has(n[0])){var i=t.get(n[0]),a=new Map;a.set("inputSigns",i),a.set("inputUncertaintySigns",parseUncertaintySigns),t.set(n[0],a)}else t.set(n[0],parseUncertaintySigns);for(;null!==(n=r.exec(e.inputExponentMarkers));)t.set(n[0],parseExponentMarkers);for(;null!==(n=r.exec(e.inputIgnore));)t.set(n[0],parseIgnore);return t}function parseNumber(e,n,t){var r,i,a,o,s=generateNumberMapping(t);n=(n=(n=(n=(n=n.replace("<<","\\ll")).replace(">>","\\gg")).replace("<=","\\le")).replace(">=","\\ge")).replace("+-","\\pm");var l=generateNumberPiece(),u=new mathjax_full_js_input_tex_TexParser__WEBPACK_IMPORTED_MODULE_1__.Z(n,e.stack.env,e.configuration);for(u.i=0;u.i=0;f--){0==+(l.uncertainty[f].whole+(""!=l.uncertainty[f].decimal?".":"")+l.uncertainty[f].fractional)&&l.uncertainty.splice(f,1)}return l}function processNumber(parser){var globalOptions=Object.assign({},parser.options),localOptions=(0,_options_options__WEBPACK_IMPORTED_MODULE_4__.b9)(parser);Object.assign(globalOptions,localOptions);var text=parser.GetArgument("num");if(globalOptions.parseNumbers){if(globalOptions.evaluateExpression){var expression=globalOptions.expression;expression=expression.replace("#1",text);var result=eval(expression);text=result.toString()}var num=parseNumber(parser,text,globalOptions);(0,_numPostProcessMethods__WEBPACK_IMPORTED_MODULE_3__.V)(num,globalOptions);var mmlNodes=(0,_numDisplayMethods__WEBPACK_IMPORTED_MODULE_2__.co)(num,parser,globalOptions);return mmlNodes}var mml=new mathjax_full_js_input_tex_TexParser__WEBPACK_IMPORTED_MODULE_1__.Z(text,parser.stack.env,parser.configuration).mml();return[mml]}},569:function(e,n,t){t.d(n,{V:function(){return f},c:function(){return l}});var r=t(100),i=t(759),a=t(936);function o(e,n){var t=JSON.parse(JSON.stringify(e)),r=(+(t.sign+t.whole+t.decimal+t.fractional+(""!=t.exponent?"e"+t.exponentSign+t.exponent:""))).toExponential(),o=(0,i.p3)(a.ZE,r,n),s=0;if(""!=t.fractional)for(var l=t.fractional.length-1;l>=0&&"0"==t.fractional[l];l--)s++;if(""!=t.whole&&t.fractional.length==s)for(var u=t.whole.length-1;u>=0&&"0"==t.whole[u];u--)s++;for(var c=0;c0&&(o.decimal="."),t)t[p]=o[p];return t}function s(e,n){if(null!=e){for(var t=n-+(e.exponentSign+e.exponent),r=Math.sign(t),i=0;i0?(e.whole=e.whole+e.fractional.slice(0,1),e.fractional=e.fractional.slice(1,e.fractional.length)):e.whole=e.whole+"0":e.whole.length>0?(e.fractional=e.whole.slice(e.whole.length-1,e.whole.length)+e.fractional,e.whole=e.whole.slice(0,e.whole.length-1)):e.fractional="0"+e.fractional;""!=e.fractional&&""==e.decimal&&(e.decimal="."),e.exponent=Math.abs(n).toString(),e.exponentSign=Math.sign(n)<0?"-":""}}function l(e,n){var t=o(e,n);Object.assign(e,t),s(e,n.fixedExponent)}var u=new Map([["input",function(){}],["fixed",l],["engineering",function(e,n){var t=o(e,n);Object.assign(e,t);for(var r=+(e.exponentSign+e.exponent);r%3!=0;)r--;s(e,r)}],["scientific",function(e,n){var t=o(e,n);Object.assign(e,t)}],["threshold",function(e,n){var t=n.exponentThresholds.split(":");if(2!=t.length)throw r.W.ExponentThresholdsError(n.exponentThresholds);var i=o(e,n),a=+(i.exponentSign+i.exponent);a>+t[0]&&a<+t[1]||Object.assign(e,i)}]]);function c(e,n,t){var r=!1;return n>5?r=!0:5==n&&(r=!t||e%2!=0),r}function p(e,n){var t="",r=new Array,i=+e[n]+1,a=0==i;r.push(i);for(var o=n-1;o>=0;o--)a?(a=0==(i=+e[o]+1),r.push(i)):(i=+e[o],r.push(i));return r.reverse(),r.forEach((function(e){return t+=e})),t}function _(e,n){if(0==Math.abs(+(e.whole+e.decimal+e.fractional+(""!=e.exponentMarker?"e":"")+e.exponentSign+e.exponent)))if("0"!=n.roundMinimum){e.prefix="\\lt";var t=(0,i.p3)(a.ZE,n.roundMinimum,n);e.sign=t.sign,e.whole=t.whole,e.decimal=t.decimal,e.fractional=t.fractional,e.exponentMarker=t.exponentMarker,e.exponentSign=t.exponentSign,e.exponent=t.exponent}else n.roundZeroPositive&&(e.sign="")}var m=new Map([["none",function(){}],["places",function(e,n){if(0==e.uncertainty.length){if(e.fractional.length>n.roundPrecision){var t=+e.fractional.slice(n.roundPrecision,n.roundPrecision+1),r=+e.fractional.slice(n.roundPrecision-1,n.roundPrecision),i="0"===e.whole?0:e.whole.length;if(c(r,t,"even"==n.roundHalf)){var a=p(("0"===e.whole?"":e.whole)+e.fractional,i+n.roundPrecision-1);e.whole=a.slice(0,i),e.fractional=a.slice(i,a.length)}else e.fractional=e.fractional.slice(0,n.roundPrecision)}else if(e.fractional.lengthn.roundPrecision){var r,i=+t.slice(n.roundPrecision,n.roundPrecision+1);r=c(+t.slice(n.roundPrecision-1,n.roundPrecision),i,"even"==n.roundHalf)?p(t,n.roundPrecision-1):t.slice(0,n.roundPrecision);var a="0"===e.whole?0:e.whole.length;if(r.length>=a)e.fractional=0==a?"".padEnd(e.fractional.length-(+e.fractional).toString().length,"0"):"",e.fractional+=r.slice(a,r.length);else{e.fractional="",e.decimal="";var o=a-r.length;e.whole=r;for(var s=0;s0){e.uncertainty.forEach((function(e){if("pm"==e.type&&+(e.whole+e.decimal+e.fractional)<1){for(var n=0,t=0;t0){var r=+e.whole.slice(n.roundPrecision,n.roundPrecision+1);c(+e.whole.slice(n.roundPrecision-1,n.roundPrecision),r,"even"==n.roundHalf)?e.whole=p(e.whole,n.roundPrecision-1):e.whole=e.whole.slice(0,n.roundPrecision)}}));var r=t-n.roundPrecision;if(r>0){var i,a=e.whole+e.fractional,o=a.length-r,s=+a.slice(o,o+1);if((i=c(+a.slice(o-1,o),s,"even"==n.roundHalf)?p(a,o-1):a.slice(0,o)).length>=e.whole.length)e.fractional=i.slice(e.whole.length,i.length);else{e.fractional="",e.decimal="";var l=e.whole.length-i.length;e.whole=i;for(var u=0;u0&&e.uncertainty[0].fractional.length>e.fractional.length&&(""===e.decimal&&(e.decimal="."),e.fractional=e.fractional.padEnd(e.uncertainty[0].fractional.length,"0")),n.dropUncertainty&&e.uncertainty.splice(0,e.uncertainty.length),n.dropExponent&&(e.exponentMarker="",e.exponentSign="",e.exponent=""),m.get(n.roundMode)(e,n),n.dropZeroDecimal&&0==+e.fractional&&(e.fractional="",e.decimal=""),n.minimumIntegerDigits>0){var t=n.minimumIntegerDigits-e.whole.length;if(t>0)for(var r=0;r0){var i=n.minimumDecimalDigits-e.fractional.length;if(i>0)for(var a=0;a\\approx\\ge\\geq\\gg\\le\\leq\\ll\\sim",inputDecimalMarkers:".,",inputDigits:"0123456789",inputExponentMarkers:"dDeE",inputIgnore:"",inputOpenUncertainty:"(",inputSigns:"+-\\pm\\mp",inputUncertaintySigns:"\\pm\\mp",parseNumbers:!0,retainExplicitDecimalMarker:!1,retainExplicitPlus:!1,retainNegativeZero:!1,retainZeroUncertainty:!1}),{dropExponent:!1,dropUncertainty:!1,dropZeroDecimal:!1,exponentMode:"input",exponentThresholds:"-3:3",fixedExponent:0,minimumIntegerDigits:0,minimumDecimalDigits:0,roundHalf:"up",roundMinimum:"0",roundMode:"none",roundPad:!0,roundPrecision:2,roundZeroPositive:!0}),{bracketAmbiguousNumbers:!0,bracketNegativeNumbers:!1,digitGroupSize:3,digitGroupFirstSize:-1,digitGroupOtherSize:-1,exponentBase:"10",exponentProduct:"\\times",groupDigits:"all",groupMinimumDigits:5,groupSeparator:"\\,",negativeColor:"",outputCloseUncertainty:")",outputDecimalMarker:".",outputExponentMarker:"",outputOpenUncertainty:"(",printImplicitPlus:!1,printUnityMantissa:!0,printZeroExponent:!1,printZeroInteger:!0,tightSpacing:!1,uncertaintyDescriptorMode:"bracket-separator",uncertaintyDescriptorSeparator:"\\",uncertaintyDescriptors:"",uncertaintyMode:"compact",uncertaintySeparator:"",zeroDecimalAsSymbol:!1,zeroSymbol:"\\mbox{---}"}),o=Object.assign(Object.assign({},a),{angleMode:"input",angleSymbolDegree:"\\degree",angleSymbolMinute:"'",angleSymbolOverDecimal:!1,angleSymbolSecond:"''",angleSeparator:"",fillAngleDegrees:!1,fillAngleMinutes:!1,fillAngleSeconds:!1,numberAngleProduct:""}),s=Object.assign(Object.assign(Object.assign({},a),i),{allowQuantityBreaks:!1,extractMassInKilograms:!0,prefixMode:"input",quantityProduct:"\\,",separateUncertaintyUnits:"bracket"});function l(e,n){var t="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!t){if(Array.isArray(e)||(t=function(e,n){if(!e)return;if("string"==typeof e)return u(e,n);var t=Object.prototype.toString.call(e).slice(8,-1);"Object"===t&&e.constructor&&(t=e.constructor.name);if("Map"===t||"Set"===t)return Array.from(e);if("Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t))return u(e,n)}(e))||n&&e&&"number"==typeof e.length){t&&(e=t);var r=0,i=function(){};return{s:i,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,o=!0,s=!1;return{s:function(){t=t.call(e)},n:function(){var e=t.next();return o=e.done,e},e:function(e){s=!0,a=e},f:function(){try{o||null==t.return||t.return()}finally{if(s)throw a}}}}function u(e,n){(null==n||n>e.length)&&(n=e.length);for(var t=0,r=new Array(n);t=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,o=!0,s=!1;return{s:function(){t=t.call(e)},n:function(){var e=t.next();return o=e.done,e},e:function(e){s=!0,a=e},f:function(){try{o||null==t.return||t.return()}finally{if(s)throw a}}}}function _unsupportedIterableToArray(e,n){if(e){if("string"==typeof e)return _arrayLikeToArray(e,n);var t=Object.prototype.toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?_arrayLikeToArray(e,n):void 0}}function _arrayLikeToArray(e,n){(null==n||n>e.length)&&(n=e.length);for(var t=0,r=new Array(n);t=a))break;r=s}}catch(e){o.e(e)}finally{o.f()}var l=n[0];if(""!=l.prefix){var u=(null!=l.power?+l.power:1)*("denominator"==l.position?-1:1);for(r+=(l.prefix?_units__WEBPACK_IMPORTED_MODULE_6__.g5.get(l.prefix):1)*u;!_units__WEBPACK_IMPORTED_MODULE_6__.g5.revHas(r);)r++}l.prefix=_units__WEBPACK_IMPORTED_MODULE_6__.g5.revGet(r);var c=r-a;e.exponent=Math.abs(c).toString(),e.exponentSign=Math.sign(c)>0?"":"-",(0,_numPostProcessMethods__WEBPACK_IMPORTED_MODULE_3__.c)(e,t)}}function extractExponent(e,n,t){if(null!=n){for(var r=0,i=0;i0?"":"-",""==e.exponentMarker&&(e.exponentMarker="e")}}var prefixModeMap=new Map([["input",function(){}],["combine-exponent",combineExponent],["extract-exponent",extractExponent]]);function findUncertaintyNode(e){var n,t=_createForOfIteratorHelper(e.childNodes);try{for(t.s();!(n=t.n()).done;){var r=n.value;if(r){if(null!=r.attributes)if(-1!=r.attributes.getExplicitNames().indexOf("data-siunitx-uncertainty"))return r;var i=findUncertaintyNode(r);if(null!=i)return i}}}catch(e){t.e(e)}finally{t.f()}return null}var separateUncertaintyUnitsMmlMap=new Map([["single",function(e,n,t,r,i){return[].concat(_toConsumableArray(e),[t,n])}],["bracket",function(e,n,t,r,i){var a,o=null,s=_createForOfIteratorHelper(e);try{for(s.s();!(a=s.n()).done;){var l=findUncertaintyNode(a.value);if(null!=l){o=l;break}}}catch(e){s.e(e)}finally{s.f()}if(null!=o){var u=r.create("token","mo",{},i.outputOpenUncertainty),c=r.create("token","mo",{},i.outputCloseUncertainty);return[u].concat(_toConsumableArray(e),[c,t,n])}return[].concat(_toConsumableArray(e),[t,n])}],["repeat",function(e,n,t,r,i){var a,o=null,s=_createForOfIteratorHelper(e);try{for(s.s();!(a=s.n()).done;){var l=findUncertaintyNode(a.value);if(null!=l){o=l;break}}}catch(e){s.e(e)}finally{s.f()}if(null!=o){var u=o.parent,c=u.childNodes.indexOf(o);return u.childNodes.splice(c,0,t,n),u.appendChild(t),u.appendChild(n),_toConsumableArray(e)}return[].concat(_toConsumableArray(e),[t,n])}]]),separateUncertaintyUnitsMap=new Map([["single",function(e,n,t){return e+t.quantityProduct+n}],["bracket",function(e,n,t){return-1==e.indexOf("\\pm")?e+t.quantityProduct+n:t.outputOpenUncertainty+e+t.outputCloseUncertainty+t.quantityProduct+n}],["repeat",function(e,n,t){for(var r=e.split("\\pm"),i="",a=0;a1){var i=60*+("."+r[1]);e.seconds=(0,l.Fw)(),e.seconds.whole=Math.floor(i).toString();var a=(i+"").split(".");a.length>1&&(e.seconds.decimal=".",e.seconds.fractional=a[1])}}}}],["decimal",function(e){var n=0;null!=e.seconds&&(n=+e.seconds.whole/60,e.seconds=null),null!=e.minutes&&(n=(+e.minutes.whole+n)/60,e.minutes=null);var t=((n=+e.degrees.whole+n)+"").split(".");e.degrees.whole=t[0],t.length>1&&(e.degrees.decimal=".",e.degrees.fractional=t[1])}]]);function _(e){var n=Object.assign({},e.options),t=(0,u.b9)(e);Object.assign(n,t);var r=e.GetArgument("ang"),i=c(e,r,n);p.get(n.angleMode)(i);var o=function(e,n){var t="",r=+(e.degrees.whole+(""!=e.degrees.decimal?".":"")+e.degrees.fractional);if(""==e.degrees.whole&&n.fillAngleDegrees&&("-"==e.minutes.sign?(e.degrees.sign="-",e.minutes.sign=""):"-"==e.seconds.sign&&(e.degrees.sign="-",e.seconds.sign=""),e.degrees.whole="0"),0!=r||"0"==e.degrees.whole||n.fillAngleDegrees)if(n.angleSymbolOverDecimal){var i=(0,s.S1)(e.degrees,n),a=i.split(n.outputDecimalMarker);a.length>1?(t+=a[0],t+="\\rlap{"+n.outputDecimalMarker+"}{\\class{MathML-Unit}{\\mathrm{"+n.angleSymbolDegree+"}}}",t+=a[1]):(t+=i,t+=n.numberAngleProduct,t+="\\class{MathML-Unit}{\\mathrm{"+n.angleSymbolDegree+"}}")}else t+=(0,s.S1)(e.degrees,n),t+=n.numberAngleProduct,t+="\\mathrm{"+n.angleSymbolDegree+"}";if(""!=t&&""!=n.angleSeparator&&(t+=n.angleSeparator),null!=e.minutes){var o=+(e.minutes.whole+(""!=e.minutes.decimal?".":"")+e.minutes.fractional),l="\\mathrm{"+n.angleSymbolMinute+"}";if("\\mathrm{'}"===l&&(l=1==o?"\\arialabel{degree-minute}{\\degreeminute}":"\\arialabel{degree-minutes}{\\degreeminute}"),0!=o||"0"==e.minutes.whole||n.fillAngleMinutes)if(0==o&&n.fillAngleMinutes&&("-"==e.seconds.sign&&(e.minutes.sign="-",e.seconds.sign=""),e.minutes.whole="0"),n.angleSymbolOverDecimal){var u=(0,s.S1)(e.minutes,n),c=u.split(n.outputDecimalMarker);c.length>1?(t+=c[0],t+="\\rlap{"+n.outputDecimalMarker+"}{"+l+"}",t+=c[1]):(t+=u,t+=n.numberAngleProduct,t+=l)}else t+=(0,s.S1)(e.minutes,n),t+=n.numberAngleProduct,t+=l}if(""==t||""==n.angleSeparator||t.endsWith(n.angleSeparator)||(t+=n.angleSeparator),null!=e.seconds){var p=+(e.seconds.whole+(""!=e.seconds.decimal?".":"")+e.seconds.fractional),_="\\mathrm{"+n.angleSymbolSecond+"}";if("\\mathrm{''}"===_&&(_=1==p?"\\arialabel{degree-second}{\\degreesecond}":"\\arialabel{degree-seconds}{\\degreesecond}"),0!=p||"0"==e.seconds.whole||n.fillAngleSeconds)if(0==p&&n.fillAngleSeconds&&(e.seconds.whole="0"),n.angleSymbolOverDecimal){var m=(0,s.S1)(e.seconds,n),f=m.split(n.outputDecimalMarker);f.length>1?(t+=f[0],t+="\\rlap{"+n.outputDecimalMarker+"}{"+_+"}",t+=f[1]):(t+=m,t+=n.numberAngleProduct,t+=_)}else t+=(0,s.S1)(e.seconds,n),t+=n.numberAngleProduct,t+=_}return t}(i,n);return new a.Z(o,e.stack.env,e.configuration).mml()}var m=t(679),f=t(489),g=t(763),d=t(196),h=function(e,n){var t=e.ParseArg(n);if(!d.Z.isInferred(t))return t;var r=d.Z.getChildren(t);if(1===r.length)return r[0];var i=e.create("node","mrow");return d.Z.copyChildren(t,i),d.Z.copyAttributes(t,i),i},M=t(564),b=t(569),v={real:Object.assign({},l.$4),imaginary:Object.assign({},l.$4),inputMode:"cartesian"};function x(e,n,t){var r=Object.assign({},v);if(n.includes(":")){r.inputMode="polar";var i=n.match(/(.+):(.+)/);r.real=(0,l.p3)(e,i[1],t),r.imaginary=(0,l.p3)(e,i[2],t)}else{var a=new RegExp("[".concat(t.inputComplexRoot,"]"));if(n.match(a)){var o=/[+-](?![^+-]*[+-])/,s=n.match(o),u=n.split(o);if(u.length>1){r.real=(0,l.p3)(e,u[0],t);var c=u[1].replace(a,"").trim();""===c&&(c="1"),r.imaginary=(0,l.p3)(e,s+c,t)}else{var p=u[0].replace(a,"");""===p&&(p="1"),r.imaginary=(0,l.p3)(e,p,t)}}else r.real=(0,l.p3)(e,n,t)}return r}function y(e,n,t){var r=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],i=(0,l.Yc)(n.real),a=(0,l.Yc)(n.imaginary),o=Math.hypot(i,a),s=Math.atan2(a,i);r&&(s=s/2/Math.PI*360),n.real=(0,l.p3)(e,o.toString(),t),n.imaginary=(0,l.p3)(e,s.toString(),t)}function E(e,n,t){var r=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],i=(0,l.Yc)(n.real),a=(0,l.Yc)(n.imaginary);r&&(a=2*a*Math.PI/360);var o=i*Math.cos(a),s=i*Math.sin(a);n.real=(0,l.p3)(e,o.toString(),t),n.imaginary=(0,l.p3)(e,s.toString(),t)}function O(e,n,t){var r=(0,s.co)(e.real,n,t),i=[];r.forEach((function(e){i.push(e)}));var o=(0,l.Yc)(e.imaginary);if(0!==o)if("polar"===e.inputMode&&"input"===t.complexMode||"polar"===t.complexMode){var u=new a.Z(t.complexSymbolAngle,n.stack.env,n.configuration).mml();if(i.push(u),(0,s.co)(e.imaginary,n,t).forEach((function(e){i.push(e)})),"degrees"===t.complexAngleUnit){var c=new a.Z(t.complexSymbolDegree,n.stack.env,n.configuration).mml();i.push(c)}}else{var p="-"===e.imaginary.sign?"-":"+";if(e.imaginary.sign="",0!==(0,l.Yc)(e.real)||"-"===p){var _=new a.Z(p,n.stack.env,n.configuration).mml();i.push(_)}if("before-number"===t.complexRootPosition){var m=new a.Z(t.outputComplexRoot,n.stack.env,n.configuration).mml();i.push(m)}if(1!==o||t.printComplexUnity)(0,s.co)(e.imaginary,n,t).forEach((function(e){i.push(e)}));if("after-number"===t.complexRootPosition){var f=new a.Z(t.outputComplexRoot,n.stack.env,n.configuration).mml();i.push(f)}}return i}var P=t(286),w=t(599);var D=t(278);var k,A=t(460),S={"\\num":function(e){(0,l.tL)(e).forEach((function(n){e.Push(n)}))},"\\ang":function(e){var n=_(e);e.Push(n)},"\\unit":function(e){var n=(0,f.PM)(e);e.Push(n)},"\\qty":function(e){(0,m.Pm)(e)},"\\numlist":function(e){(0,P.e5)(e)},"\\qtylist":function(e){(0,D._)(e)},"\\numproduct":function(e){(0,w._)(e)},"\\qtyproduct":function(e){(0,A.E)(e)},"\\numrange":function(e){!function(e){var n=Object.assign({},e.options),t=(0,u.b9)(e);Object.assign(n,t);var r=e.GetArgument("firstNum"),i=e.GetArgument("lastNum");if(n.parseNumbers){var o=(0,l.p3)(e,r,n),c=(0,l.p3)(e,i,n);if("individual"===n.rangeExponents)(0,b.V)(o,n),(0,b.V)(c,n);else{var p=o.exponentSign+o.exponent,_=Object.assign(n,{exponentMode:"fixed",fixedExponent:p});(0,b.V)(o,n),(0,b.V)(c,_)}var m=P.OH.get(n.rangeExponents)([o,c],e,n),f=(0,s.co)(m.numbers[0],e,n),g=new a.Z("\\text{".concat(n.rangePhrase,"}"),e.stack.env,e.configuration).mml(),d=(0,s.co)(m.numbers[1],e,n),h=[];m.leading&&h.push(m.leading),h=h.concat(f).concat(g).concat(d),m.trailing&&(h=h.concat(m.trailing)),h.forEach((function(n){e.Push(n)}))}else{var M=new a.Z(r+i,e.stack.env,e.configuration).mml();e.Push(M)}}(e)},"\\qtyrange":function(e){!function(e){var n=Object.assign({},e.options),t=(0,u.b9)(e);Object.assign(n,t);var r=e.GetArgument("firstNum"),i=e.GetArgument("lastNum"),o=e.GetArgument("unit"),s=-1==o.indexOf("\\"),c=(0,f.n2)(e,o,n,t,s);if(n.parseNumbers){var p=(0,l.p3)(e,r,n),_=(0,l.p3)(e,i,n);if("individual"===n.rangeExponents)(0,b.V)(p,n),(0,b.V)(_,n);else{var g=p.exponentSign+p.exponent,d=Object.assign(n,{exponentMode:"fixed",fixedExponent:g});(0,b.V)(p,n),(0,b.V)(_,d)}var h=(0,f.Ho)(e,c,n,s),M=[new a.Z(h,e.stack.env,e.configuration).mml()],v=(0,m.mD)(e,n);v&&M.splice(0,0,v);var x=P.OH.get(n.rangeExponents)([p,_],e,n),y=D.N.get(n.rangeUnits)(x,M,e,n),E=new a.Z("\\text{".concat(n.rangePhrase,"}"),e.stack.env,e.configuration).mml(),O=[];x.leading&&O.push(x.leading),O=O.concat(y.numbers[0]).concat(E).concat(y.numbers[1]),x.trailing&&(O=O.concat(x.trailing)),O.forEach((function(n){e.Push(n)}))}else{var w=new a.Z(r+i,e.stack.env,e.configuration).mml();e.Push(w)}}(e)},"\\complexnum":function(e){var n=function(e){var n=Object.assign({},e.options),t=(0,u.b9)(e);Object.assign(n,t);var r=e.GetArgument("complexnum");if(n.parseNumbers){var i=x(e,r,n);return"polar"===n.complexMode&&"polar"!==i.inputMode?y(e,i,n,"degrees"===n.complexAngleUnit):"cartesian"===n.complexMode&&"cartesian"!==i.inputMode&&E(e,i,n,"degrees"===n.complexAngleUnit),(0,b.V)(i.real,n),(0,b.V)(i.imaginary,n),O(i,e,n)}return[new a.Z(r,e.stack.env,e.configuration).mml()]}(e);n.forEach((function(n){e.Push(n)}))},"\\complexqty":function(e){!function(e){var n,t,r=Object.assign({},e.options),i=(0,u.b9)(e);Object.assign(r,i);var o,l=e.GetArgument("complexnum"),c=e.GetArgument("unit"),p=-1==c.indexOf("\\"),_=(0,f.n2)(e,c,r,i,p),g=x(e,l,r);"polar"===r.complexMode&&"polar"!==g.inputMode?y(e,g,r):"cartesian"===r.complexMode&&"cartesian"!==g.inputMode&&E(e,g,r),null===(n=m.pk.get(r.prefixMode))||void 0===n||n(g.real,_,r),null===(t=m.pk.get(r.prefixMode))||void 0===t||t(g.imaginary,_,r),(0,b.V)(g.real,r),(0,b.V)(g.imaginary,r),O(g,e,r).forEach((function(n){e.Push(n)}));var d=null,h=r.quantityProduct.trimStart();if(""!==h){var M=s.G2[h];if(void 0===M){var v=new a.Z(M,k.stack.env,k.configuration).mml();M=(0,s.uI)(v)}d=e.create("token","mo",{},M)}e.Push(d),o=(0,f.Ho)(e,_,r,p);var P=new a.Z(o,e.stack.env,e.configuration).mml();e.Push(P)}(e)},"@{}S":function(e){},"\\tablenum":function(e){},"\\sisetup":function(e){(0,u.hD)(e)}},C={"\\DeclareSIUnit":function(e,n,t){var r=e.configuration.packageData.get(U),i=e.configuration.packageData.get(T),a=e.GetArgument(n),o=e.GetArgument(n);r.set(a,o),void 0!==t&&i.set(a,t)},"\\DeclareSIQualifier":function(e,n,t){},"\\DeclareSIPower":function(e,n,t){}},U="siunitxUnits",T="siunitxUnitOptions";new i.eJ("angchar-symbols",(function(e,n){var t=n.attributes||{};t.mathvariant=M.d.Variant.NORMAL,t.class="MathML-Unit";var r=e.create("token","mi",{"data-semantic-type":"empty"}),i=e.create("token","mi",t,n.char),a=e.create("node","msup",[r,i]);e.Push(a)}),{degreeminute:["\u2032",{}],degreesecond:["\u2033",{}]}),new i.QQ("siunitxMap",{num:["siunitxToken","num"],ang:["siunitxToken","ang"],complexnum:["siunitxToken","complexnum"],unit:["siunitxToken","unit"],qty:["siunitxToken","qty"],complexqty:["siunitxToken","complexqty"],numlist:["siunitxToken","numlist"],numproduct:["siunitxToken","numproduct"],numrange:["siunitxToken","numrange"],qtylist:["siunitxToken","qtylist"],qtyrange:["siunitxToken","qtyrange"],qtyproduct:["siunitxToken","qtyproduct"],DeclareSIUnit:["siunitxGlobal","DeclareSIUnit"],sisetup:["siunitxToken","sisetup"],arialabel:["Arialabel","arialabel"],data:["Dataset","data"]},{siunitxToken:function(e,n){var t;k=e,null===(t=S[n])||void 0===t||t.call(S,e)},siunitxGlobal:function(e,n){var t;k=e;var r=(0,u.b9)(e);null===(t=C[n])||void 0===t||t.call(C,e,n,r)},Arialabel:function(e,n){var t=e.GetArgument(n),r=h(e,n);d.Z.setAttribute(r,"aria-label",t),e.Push(r)},Dataset:function(e,n){var t=e.GetArgument(n),r=h(e,n),i=t.split("=");d.Z.setAttribute(r,"data-"+i[0],i[1]),e.Push(r)}});r.VK.create("siunitx",{handler:{macro:["angchar-symbols","siunitxMap"]},options:u.Sk,config:function(e,n){n.parseOptions.packageData.set(U,g.bs),n.parseOptions.packageData.set(T,g.Zx)}})},489:function(e,n,t){t.d(n,{Ho:function(){return d},PM:function(){return M},n2:function(){return h}});var r=t(516),i=t(693),a=t(100),o=t(124),s=t(936),l=t(763);function u(e){return function(e){if(Array.isArray(e))return c(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,n){if(!e)return;if("string"==typeof e)return c(e,n);var t=Object.prototype.toString.call(e).slice(8,-1);"Object"===t&&e.constructor&&(t=e.constructor.name);if("Map"===t||"Set"===t)return Array.from(e);if("Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t))return c(e,n)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function c(e,n){(null==n||n>e.length)&&(n=e.length);for(var t=0,r=new Array(n);t2&&void 0!==arguments[2]&&arguments[2],i="";e.cancel&&(i+="\\cancel{"),e.highlight&&(i+="{\\color{".concat(e.highlight,"}")),i+=n.unitFontCommand+"{",n.powerHalfAsSqrt&&e.power&&.5==e.power?(i+="\\sqrt{\\class{MathML-Unit}{".concat(e.prefix).concat(e.symbol,"}}"),e.power=null):i+="\\class{MathML-Unit}{".concat(e.prefix).concat(e.symbol,"}"),e.qualifier&&(i+=null===(t=f.get(n.qualifierMode))||void 0===t?void 0:t(e.qualifier,n.qualifierPhrase)),i+="}";var a=null!=e.power?r?Math.abs(e.power*("denominator"==e.position?-1:1)):e.power*("denominator"==e.position?-1:1):r?Math.abs(1*("denominator"==e.position?-1:1)):1*("denominator"==e.position?-1:1);return null!=a&&1!=a&&(i+="^{"+a+"}"),e.cancel&&(i+="}"),e.highlight&&(i+="}"),{latex:i,superscriptPresent:1!=a}}function d(e,n,t,r){var i=!1,a="";""!=t.unitColor?(a+="{\\color{".concat(t.unitColor,"}"),i=!0):""!=t.color&&(a+="{\\color{".concat(t.color,"}"),i=!0);var o=!1;if(n.length>=2&&1==n.filter((function(e){var n=null!=e.power?e.power*("denominator"==e.position?-1:1):1;return-1==Math.sign(n)})).length&&"single-symbol"==t.perMode&&(o=!0),r){var s="",l=null;n.every((function(e){return"denominator"!==e.position||(l=e,!1)})),n.forEach((function(e){e==l&&(s+=" / ");var n=g(e,t);""!=s&&(s+=t.interUnitProduct),s+=n.latex})),a+=s}else{var u=0,c=0;if(n.forEach((function(e){"denominator"===e.position||null!=e.power&&e.power<0?c++:u++}),0),"fraction"==t.perMode||"symbol"==t.perMode||"repeated-symbol"==t.perMode||o||"single-symbol"===t.perMode&&1==c&&u>0){var p="",_="",m=!1;n.forEach((function(e){var n;"denominator"===e.position||null!=e.power&&e.power<0?(n=g(e,t,"fraction"==t.perMode||"symbol"==t.perMode||"repeated-symbol"==t.perMode||"single-symbol"==t.perMode||o),""!=_&&("repeated-symbol"==t.perMode?(n.superscriptPresent&&(_+=t.perSymbolScriptCorrection),_+=t.perSymbol):_+=t.interUnitProduct),_+=n.latex):(n=g(e,t,"fraction"==t.perMode||"symbol"==t.perMode||"repeated-symbol"==t.perMode||"single-symbol"==t.perMode||o),m=n.superscriptPresent,""!=p&&(p+=t.interUnitProduct),p+=n.latex)})),""==p&&""!=_&&(p="1"),""!=_?(c>1&&"symbol"===t.perMode&&t.bracketUnitDenominator&&(_="("+_+")"),"fraction"===t.perMode?a+=t.fractionCommand+"{"+p+"}{"+_+"}":"repeated-symbol"===t.perMode||"symbol"===t.perMode||o||"single-symbol"===t.perMode?a+=p+(m?t.perSymbolScriptCorrection:"")+t.perSymbol+_:console.log("shouldn't be here")):a+=p}else{"power-positive-first"==t.perMode&&(n=n.sort((function(e,n){var t=null!=e.power?e:1;"denominator"==e.position&&(t=-t);var r=null!=n.power?n:1;return"denominator"==n.position&&(r=-r),tr?-1:0})));var f="";n.forEach((function(e){var n=g(e,t);n.superscriptPresent,""!=f&&(f+=t.interUnitProduct),f+=n.latex})),a+=f}}return i&&(a+="}"),a}function h(e,n,t,a,o){var s=new Array;if(o)s.push.apply(s,u(function(e,n){var t=new Array,r=new i.Z(n,e.stack.env,e.configuration);r.i=0;var a={position:"numerator"},o=!1,s="";for(;r.ie.length)&&(n=e.length);for(var t=0,r=new Array(n);te.length)&&(n=e.length);for(var t=0,r=new Array(n);te.length)&&(n=e.length);for(var t=0,r=new Array(n);t=o)for(var d=-1!==c?c:t,f=-1!==p?p:t,g=i(l?[d,f+1]:[m.length-d,-f],2),h=g[0],b=g[1],v=h;v>=0&&v0?(e.fractional=e.whole.slice(r,e.whole.length),e.whole=e.whole.slice(0,r),""!==e.fractional&&(e.decimal=t["output-decimal-marker"])):r<0&&"full"===t["uncertainty-mode"]&&(e.fractional="".padEnd(Math.abs(r),"0")+e.whole,e.whole="0",e.decimal=t["output-decimal-marker"])}}else if(+(e.whole+e.decimal+e.fractional)<1){for(var i=0,a=0;a=0){for(var i=0;ie.length)&&(n=e.length);for(var t=0,r=new Array(n);t0?e.uncertainty[e.uncertainty.length-1]:e}function parseDigits(e,n){var t=getLastNumPiece(n);""!==t.exponentMarker?t.exponent+=e:""!==t.decimal?t.fractional+=e:t.whole+=e}function parseDecimals(e,n){getLastNumPiece(n).decimal+=e}function parseComparators(e,n){var t=getLastNumPiece(n);if(""!==t.prefix)throw _error_errors__WEBPACK_IMPORTED_MODULE_5__.W.ComparatorAlreadySet(t.prefix,e);t.prefix+=e}function parseExponentMarkers(e,n){n.exponentMarker+=e}function parseSigns(e,n){var t=getLastNumPiece(n);""!==t.exponentMarker?t.exponentSign+=e:t.sign+=e}function parseOpenUncertainty(e,n){var t=Object.assign(Object.assign({},UncertaintyDefault),{type:"bracket"});n.uncertainty.push(t)}function parseCloseUncertainty(e,n){if(0===n.uncertainty.length)throw new mathjax_full_js_input_tex_TexError__WEBPACK_IMPORTED_MODULE_0__.Z("50","Trying to close an uncertainty that doesn't exist.");var t=n.uncertainty[n.uncertainty.length-1];if(t.completed)throw new mathjax_full_js_input_tex_TexError__WEBPACK_IMPORTED_MODULE_0__.Z("51","Uncertainty was already closed.");t.completed=!0}function parseUncertaintySigns(e,n){var t=Object.assign(Object.assign({},UncertaintyDefault),{type:"pm"});n.uncertainty.push(t)}function parseIgnore(){}function generateNumberMapping(e){for(var n=new Map,t=/\\(?:[a-zA-Z]+|[\uD800-\uDBFF].|.)|[\uD800-\uDBFF].|[^\s\\]/g,r=function(){var r=_slicedToArray(a[i],2),o=r[0],s=r[1],l=e[o];if(l.match(/(?:^|[^\\])(?:\\\\)*\\$/))throw _error_errors__WEBPACK_IMPORTED_MODULE_5__.W.BadOptionChars(o);(l.match(t)||[]).forEach((function(e){if(n.has(e)&&"input-uncertainty-signs"===o){var t=n.get(e);n.set(e,(function(e,n){(""===n.whole&&""===n.decimal?t:parseUncertaintySigns)(e,n)}))}else n.set(e,s)}))},i=0,a=[["input-comparators",parseComparators],["input-signs",parseSigns],["input-digits",parseDigits],["input-decimal-markers",parseDecimals],["input-open-uncertainty",parseOpenUncertainty],["input-close-uncertainty",parseCloseUncertainty],["input-uncertainty-signs",parseUncertaintySigns],["input-exponent-markers",parseExponentMarkers],["input-ignore",parseIgnore]];i>","\\gg").replace("<=","\\le").replace(">=","\\ge").replace("+-","\\pm");var i,a=generateNumberPiece(),o=new mathjax_full_js_input_tex_TexParser__WEBPACK_IMPORTED_MODULE_1__.Z(n,e.stack.env,e.configuration);for(o.i=0;o.i=0;s--){0===+(a.uncertainty[s].whole+(""!==a.uncertainty[s].decimal?".":"")+a.uncertainty[s].fractional)&&a.uncertainty.splice(s,1)}return a}function processNumber(parser){var globalOptions=Object.assign({},parser.options.siunitx),localOptions=(0,_options_options__WEBPACK_IMPORTED_MODULE_4__.b9)(parser,globalOptions);Object.assign(globalOptions,localOptions);var text=parser.GetArgument("num");if(globalOptions["parse-numbers"]){if(globalOptions["evaluate-expression"]){var expression=globalOptions.expression;expression=expression.replace("#1",text),text=eval(expression).toString()}var num=parseNumber(parser,text,globalOptions);(0,_numPostProcessMethods__WEBPACK_IMPORTED_MODULE_3__.V)(parser,num,globalOptions);var mmlNode=(0,_numDisplayMethods__WEBPACK_IMPORTED_MODULE_2__.co)(num,parser,globalOptions);return mmlNode}var mml=new mathjax_full_js_input_tex_TexParser__WEBPACK_IMPORTED_MODULE_1__.Z(text,parser.stack.env,parser.configuration).mml();return mml}},569:function(e,n,t){t.d(n,{V:function(){return m},c:function(){return s}});var r=t(660),i=t(759);function a(e,n,t){var r=JSON.parse(JSON.stringify(n)),a=(+(r.sign+r.whole+r.decimal+r.fractional+(""!==r.exponent?"e"+r.exponentSign+r.exponent:""))).toExponential(),o=(0,i.p3)(e,a,t),s=0;if(""!==r.fractional)for(var l=r.fractional.length-1;l>=0&&"0"===r.fractional[l];l--)s++;if(""!==r.whole&&r.fractional.length===s)for(var u=r.whole.length-1;u>=0&&"0"===r.whole[u];u--)s++;for(var c=0;c0&&(o.decimal="."),r)r[p]=o[p];return r}function o(e,n){if(e){for(var t=n-+(e.exponentSign+e.exponent),r=Math.sign(t),i=0;i0?(e.whole=e.whole+e.fractional.slice(0,1),e.fractional=e.fractional.slice(1,e.fractional.length)):e.whole=e.whole+"0":e.whole.length>0?(e.fractional=e.whole.slice(e.whole.length-1,e.whole.length)+e.fractional,e.whole=e.whole.slice(0,e.whole.length-1)):e.fractional="0"+e.fractional;""!==e.fractional&&""===e.decimal&&(e.decimal="."),e.exponent=Math.abs(n).toString(),e.exponentSign=Math.sign(n)<0?"-":""}}function s(e,n,t){var r=a(e,n,t);Object.assign(n,r),o(n,t["fixed-exponent"])}var l=new Map([["input",function(){}],["fixed",s],["engineering",function(e,n,t){var r=a(e,n,t);Object.assign(n,r);for(var i=+(n.exponentSign+n.exponent);i%3!=0;)i--;o(n,i)}],["scientific",function(e,n,t){var r=a(e,n,t);Object.assign(n,r)}],["threshold",function(e,n,t){var i=t["exponent-thresholds"].split(":");if(2!==i.length)throw r.W.ExponentThresholdsError(t["exponent-thresholds"]);var o=a(e,n,t),s=+(o.exponentSign+o.exponent);s>+i[0]&&s<+i[1]||Object.assign(n,o)}]]);function u(e,n,t){var r=!1;return n>5?r=!0:5===n&&(r=!t||e%2!=0),r}function c(e,n){var t="",r=new Array,i=+e[n]+1,a=0===i;r.push(i);for(var o=n-1;o>=0;o--)a?(a=0===(i=+e[o]+1),r.push(i)):(i=+e[o],r.push(i));return r.reverse(),r.forEach((function(e){return t+=e})),t}function p(e,n,t){if(0===Math.abs(+(n.whole+n.decimal+n.fractional+(""!==n.exponentMarker?"e":"")+n.exponentSign+n.exponent)))if("0"!==t["round-minimum"]){n.prefix="\\lt";var r=(0,i.p3)(e,t["round-minimum"],t);n.sign=r.sign,n.whole=r.whole,n.decimal=r.decimal,n.fractional=r.fractional,n.exponentMarker=r.exponentMarker,n.exponentSign=r.exponentSign,n.exponent=r.exponent}else t["round-zero-positive"]&&(n.sign="")}var _=new Map([["none",function(){}],["places",function(e,n,t){if(0===n.uncertainty.length){if(n.fractional.length>t["round-precision"]){var r=+n.fractional.slice(t["round-precision"],t["round-precision"]+1),i=+n.fractional.slice(t["round-precision"]-1,t["round-precision"]),a="0"===n.whole?0:n.whole.length;if(u(i,r,"even"===t["round-half"])){var o=c(("0"===n.whole?"":n.whole)+n.fractional,a+t["round-precision"]-1);n.whole=o.slice(0,a),n.fractional=o.slice(a,o.length)}else n.fractional=n.fractional.slice(0,t["round-precision"])}else if(n.fractional.lengtht["round-precision"]){var i,a=+r.slice(t["round-precision"],t["round-precision"]+1);i=u(+r.slice(t["round-precision"]-1,t["round-precision"]),a,"even"===t["round-half"])?c(r,t["round-precision"]-1):r.slice(0,t["round-precision"]);var o="0"===n.whole?0:n.whole.length;if(i.length>=o)n.fractional=0===o?"".padEnd(n.fractional.length-(+n.fractional).toString().length,"0"):"",n.fractional+=i.slice(o,i.length);else{n.fractional="",n.decimal="";var s=o-i.length;n.whole=i;for(var l=0;l0){n.uncertainty.forEach((function(e){if("pm"===e.type&&+(e.whole+e.decimal+e.fractional)<1){for(var n=0,t=0;t0){var n=+e.whole.slice(t["round-precision"],t["round-precision"]+1);u(+e.whole.slice(t["round-precision"]-1,t["round-precision"]),n,"even"===t["round-half"])?e.whole=c(e.whole,t["round-precision"]-1):e.whole=e.whole.slice(0,t["round-precision"])}}));var i=r-t["round-precision"];if(i>0){var a,o=n.whole+n.fractional,s=o.length-i,l=+o.slice(s,s+1);if((a=u(+o.slice(s-1,s),l,"even"===t["round-half"])?c(o,s-1):o.slice(0,s)).length>=n.whole.length)n.fractional=a.slice(n.whole.length,a.length);else{n.fractional="",n.decimal="";var p=n.whole.length-a.length;n.whole=a;for(var _=0;_0&&n.uncertainty[0].fractional.length>n.fractional.length&&(n.decimal||(n.decimal="."),n.fractional=n.fractional.padEnd(n.uncertainty[0].fractional.length,"0")),t["drop-uncertainty"]&&n.uncertainty.splice(0,n.uncertainty.length),t["drop-exponent"]&&(n.exponentMarker="",n.exponentSign="",n.exponent=""),_.get(t["round-mode"])(e,n,t),t["drop-zero-decimal"]&&0==+n.fractional&&(n.fractional="",n.decimal=""),t["minimum-integer-digits"]>0){var r=t["minimum-integer-digits"]-n.whole.length;if(r>0)for(var i=0;i0){var a=t["minimum-decimal-digits"]-n.fractional.length;if(a>0)for(var o=0;o\\approx\\ge\\geq\\gg\\le\\leq\\ll\\sim","input-decimal-markers":".,","input-digits":"0123456789","input-exponent-markers":"dDeE","input-ignore":"","input-open-uncertainty":"(","input-signs":"+-\\pm\\mp","input-uncertainty-signs":"\\pm\\mp","parse-numbers":!0,"retain-explicit-decimal-marker":!1,"retain-explicit-plus":!1,"retain-negative-zero":!1,"retain-zero-uncertainty":!1}),{"drop-exponent":!1,"drop-uncertainty":!1,"drop-zero-decimal":!1,"exponent-mode":"input","exponent-thresholds":"-3:3","fixed-exponent":0,"minimum-integer-digits":0,"minimum-decimal-digits":0,"round-half":"up","round-minimum":"0","round-mode":"none","round-pad":!0,"round-precision":2,"round-zero-positive":!0}),{"bracket-ambiguous-numbers":!0,"bracket-negative-numbers":!1,"digit-group-size":3,"digit-group-first-size":-1,"digit-group-other-size":-1,"exponent-base":"10","exponent-product":"\\times","group-digits":"all","group-minimum-digits":5,"group-separator":"\\,","negative-color":"","output-close-uncertainty":")","output-decimal-marker":".","output-exponent-marker":"","output-open-uncertainty":"(","print-implicit-plus":!1,"print-unity-mantissa":!0,"print-zero-exponent":!1,"print-zero-integer":!0,"tight-spacing":!1,"uncertainty-descriptor-mode":"bracket-separator","uncertainty-descriptor-separator":"\\","uncertainty-descriptors":"","uncertainty-mode":"compact","uncertainty-separator":"","zero-decimal-as-symbol":!1,"zero-symbol":"\\mbox{---}"}),o=Object.assign(Object.assign({},a),{"angle-mode":"input","angle-symbol-degree":"\\degree","angle-symbol-minute":"'","angle-symbol-over-decimal":!1,"angle-symbol-second":"''","angle-separator":"","fill-angle-degrees":!1,"fill-angle-minutes":!1,"fill-angle-seconds":!1,"number-angle-product":""}),s=Object.assign(Object.assign(Object.assign({},a),i),{"allow-quantity-breaks":!1,"extract-mass-in-kilograms":!0,"prefix-mode":"input","quantity-product":"\\,","separate-uncertainty-units":"bracket"}),l=t(660);t(106);function u(e){return u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},u(e)}function c(e,n){return function(e){if(Array.isArray(e))return e}(e)||function(e,n){var t=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null==t)return;var r,i,a=[],o=!0,s=!1;try{for(t=t.call(e);!(o=(r=t.next()).done)&&(a.push(r.value),!n||a.length!==n);o=!0);}catch(e){s=!0,i=e}finally{try{o||null==t.return||t.return()}finally{if(s)throw i}}return a}(e,n)||function(e,n){if(!e)return;if("string"==typeof e)return p(e,n);var t=Object.prototype.toString.call(e).slice(8,-1);"Object"===t&&e.constructor&&(t=e.constructor.name);if("Map"===t||"Set"===t)return Array.from(e);if("Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t))return p(e,n)}(e,n)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function p(e,n){(null==n||n>e.length)&&(n=e.length);for(var t=0,r=new Array(n);te.length)&&(n=e.length);for(var t=0,r=new Array(n);t1&&void 0!==arguments[1]&&arguments[1],o={},s=e,l=!0;s;){var c=u(s,["=",","],i,l),p=a(c,3);if(t=p[0],n=p[1],s=p[2],l=!1,"="===n){var _=u(s,[","],i),m=a(_,3);r=m[0],n=m[1],s=m[2],r="false"===r||"true"===r?JSON.parse(r):r,o[t]=r}else t&&(o[t]=!0)}return o}function l(e,n){if(0===n)return e.replace(/^\s+/,"").replace(/([^\\\s]|^)((?:\\\\)*(?:\\\s)?)?\s+$/,"$1$2");for(;n>0;)e=e.trim().slice(1,-1),n--;return e}function u(e,n){for(var t=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=e.length,o=0,s="",u=0,c=0,p=!0;uo&&(c=o),p=!1}s+=_}if(o)throw new r.Z("ExtraOpenMissingClose","Extra open brace or missing close brace");return i&&c?["","",l(s,1)]:[l(s,t?Math.min(1,c):c),"",e.slice(u)]}t(128).Z.keyvalOptions=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,t=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=s(e,i);if(n)for(var o=0,l=Object.keys(a);o2&&void 0!==arguments[2]&&arguments[2];if("["!==this.GetNext())return n;for(var i=++this.i,a=0,o=0;this.i=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,o=!0,s=!1;return{s:function(){t=t.call(e)},n:function(){var e=t.next();return o=e.done,e},e:function(e){s=!0,a=e},f:function(){try{o||null==t.return||t.return()}finally{if(s)throw a}}}}function _unsupportedIterableToArray(e,n){if(e){if("string"==typeof e)return _arrayLikeToArray(e,n);var t=Object.prototype.toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?_arrayLikeToArray(e,n):void 0}}function _arrayLikeToArray(e,n){(null==n||n>e.length)&&(n=e.length);for(var t=0,r=new Array(n);t=o))break;i=l}}catch(e){s.e(e)}finally{s.f()}var u=t[0];if(""!==u.prefix){var c=(null!==u.power?+u.power:1)*("denominator"===u.position?-1:1);for(i+=(u.prefix?_units__WEBPACK_IMPORTED_MODULE_6__.g5.get(u.prefix):1)*c;!_units__WEBPACK_IMPORTED_MODULE_6__.g5.revHas(i);)i++}u.prefix=_units__WEBPACK_IMPORTED_MODULE_6__.g5.revGet(i);var p=i-o;n.exponent=Math.abs(p).toString(),n.exponentSign=Math.sign(p)>0?"":"-",(0,_numPostProcessMethods__WEBPACK_IMPORTED_MODULE_3__.c)(e,n,r)}}function extractExponent(e,n,t,r){if(null!==t){for(var i=0,a=0;a0?"":"-",n.exponentMarker||(n.exponentMarker="e")}}var prefixModeMap=new Map([["input",function(){}],["combine-exponent",combineExponent],["extract-exponent",extractExponent]]);function findUncertaintyNode(e){var n,t=_createForOfIteratorHelper(e.childNodes);try{for(t.s();!(n=t.n()).done;){var r=n.value;if(r){if(null!==r.attributes)if(-1!==r.attributes.getExplicitNames().indexOf("data-siunitx-uncertainty"))return r;var i=findUncertaintyNode(r);if(null!==i)return i}}}catch(e){t.e(e)}finally{t.f()}return null}var separateUncertaintyUnitsMmlMap=new Map([["single",function(e,n,t,r,i){var a=r.create("node","inferredMrow",[],{});return a.appendChild(e),a.appendChild(t),a.appendChild(n),a}],["bracket",function(e,n,t,r,i){var a,o=r.create("node","inferredMrow",[],{}),s=null,l=_createForOfIteratorHelper(e.childNodes);try{for(l.s();!(a=l.n()).done;){var u=findUncertaintyNode(a.value);if(null!==u){s=u;break}}}catch(e){l.e(e)}finally{l.f()}if(null!==s){var c=r.create("token","mo",{},i["output-open-uncertainty"]),p=r.create("token","mo",{},i["output-close-uncertainty"]);return o.appendChild(c),o.appendChild(e),o.appendChild(p),o.appendChild(t),o.appendChild(n),o}return o.appendChild(e),o.appendChild(t),o.appendChild(n),o}],["repeat",function(e,n,t,r,i){var a,o=null,s=_createForOfIteratorHelper(e.childNodes);try{for(s.s();!(a=s.n()).done;){var l=findUncertaintyNode(a.value);if(null!==l){o=l;break}}}catch(e){s.e(e)}finally{s.f()}if(null!==o){var u=o.parent,c=u.childNodes.indexOf(o);return t?(u.childNodes.splice(c,0,t,n),u.appendChild(t),u.appendChild(n)):(u.childNodes.splice(c,0,n),u.appendChild(n)),e}var p=r.create("node","inferredMrow",[],{});return p.appendChild(e),p.appendChild(t),p.appendChild(n),p}]]),separateUncertaintyUnitsMap=new Map([["single",function(e,n,t){return e+t["quantity-product"]+n}],["bracket",function(e,n,t){return-1===e.indexOf("\\pm")?e+t["quantity-product"]+n:t["output-open-uncertainty"]+e+t["output-close-uncertainty"]+t["quantity-product"]+n}],["repeat",function(e,n,t){for(var r=e.split("\\pm"),i="",a=0;a0){var l=s[0],u=l.childNodes[0].getText().split(r["output-decimal-marker"]);if(u.length>1){var c=e.create("node","inferredMrow",[],{});c.appendChild(e.create("token","mn",{},u[0]));var p=e.create("node","mover",[],{accent:i});c.appendChild(p),p.appendChild(e.create("token","mo",{},".")),p.appendChild(new a.Z("\\class{MathML-Unit}{\\mathrm{"+t+"}}",e.stack.env,e.configuration).mml()),c.appendChild(e.create("token","mn",{},u[1])),l.parent.replaceChild(c,l),o=n}}return o}var _=new Map([["input",function(){}],["arc",function(e){if(null===e.minutes&&null===e.seconds&&""!==e.degrees.decimal){var n=+("0."+e.degrees.fractional);if(e.degrees.fractional="",e.degrees.decimal="",0!==n){var t=60*n;e.minutes=(0,l.Fw)(),e.minutes.whole=Math.floor(t).toString();var r=(t+"").split(".");if(r.length>1){var i=60*+("."+r[1]);e.seconds=(0,l.Fw)(),e.seconds.whole=Math.floor(i).toString();var a=(i+"").split(".");a.length>1&&(e.seconds.decimal=".",e.seconds.fractional=a[1])}}}}],["decimal",function(e){var n=0;void 0!==e.seconds&&null!==e.seconds&&(n=+e.seconds.whole/60,e.seconds=null),void 0!==e.minutes&&null!==e.minutes&&(n=(+e.minutes.whole+n)/60,e.minutes=null);var t=((n=+e.degrees.whole+n)+"").split(".");e.degrees.whole=t[0],t.length>1&&(e.degrees.decimal=".",e.degrees.fractional=t[1])}]]);function m(e){var n=Object.assign({},e.options.siunitx),t=(0,u.b9)(e,n);Object.assign(n,t);var r=e.GetArgument("ang"),i=c(e,r,n);_.get(n["angle-mode"])(i);var o=function(e,n,t){var r,i,o,l=e.create("node","inferredMrow",[],{}),u=+(n.degrees.whole+(""!==n.degrees.decimal?".":"")+n.degrees.fractional);if(!n.degrees.whole&&t["fill-angle-degrees"]&&("-"===n.minutes.sign?(n.degrees.sign="-",n.minutes.sign=""):"-"===n.seconds.sign&&(n.degrees.sign="-",n.seconds.sign=""),n.degrees.whole="0"),0!==u||"0"===n.degrees.whole||t["fill-angle-degrees"]){var c=(0,s.rW)(n.degrees,e,t);t["angle-symbol-over-decimal"]&&(r=p(e,c,t["angle-symbol-degree"],t,!0)),r||((r=e.create("node","inferredMrow",[],{})).appendChild(c),r.appendChild(new a.Z(t["number-angle-product"]+"\\class{MathML-Unit}{\\mathrm{"+t["angle-symbol-degree"]+"}}",e.stack.env,e.configuration).mml()))}if(void 0!==n.minutes&&null!==n.minutes){var _=+(n.minutes.whole+(""!==n.minutes.decimal?".":"")+n.minutes.fractional),m="\\mathrm{"+t["angle-symbol-minute"]+"}";if("\\mathrm{'}"===m&&(m=1===_?"\\arialabel{degree-minute}{\\degreeminute}":"\\arialabel{degree-minutes}{\\degreeminute}"),0!==_||"0"===n.minutes.whole||t["fill-angle-minutes"]){0===_&&t["fill-angle-minutes"]&&("-"===n.seconds.sign&&(n.minutes.sign="-",n.seconds.sign=""),n.minutes.whole="0");var d=(0,s.rW)(n.minutes,e,t);t["angle-symbol-over-decimal"]&&(i=p(e,d,m,t,!1)),i||((i=e.create("node","inferredMrow",[],{})).appendChild(d),i.appendChild(new a.Z(t["number-angle-product"]+"\\class{MathML-Unit}{\\mathrm{"+m+"}}",e.stack.env,e.configuration).mml()))}}if(void 0!==n.seconds&&null!==n.seconds){var f=+(n.seconds.whole+(""!==n.seconds.decimal?".":"")+n.seconds.fractional),g="\\mathrm{"+t["angle-symbol-second"]+"}";if("\\mathrm{''}"===g&&(g=1===f?"\\arialabel{degree-second}{\\degreesecond}":"\\arialabel{degree-seconds}{\\degreesecond}"),0!==f||"0"===n.seconds.whole||t["fill-angle-seconds"]){0===f&&t["fill-angle-seconds"]&&(n.seconds.whole="0");var h=(0,s.rW)(n.seconds,e,t);t["angle-symbol-over-decimal"]&&(o=p(e,h,g,t,!1)),o||((o=e.create("node","inferredMrow",[],{})).appendChild(h),o.appendChild(new a.Z(t["number-angle-product"]+"\\class{MathML-Unit}{\\mathrm{"+g+"}}",e.stack.env,e.configuration).mml()))}}return r&&l.appendChild(r),r&&(i||o)&&""!==t["angle-separator"]&&l.appendChild(new a.Z(t["angle-separator"],e.stack.env,e.configuration).mml()),i&&l.appendChild(i),i&&o&&""!==t["angle-separator"]&&l.appendChild(new a.Z(t["angle-separator"],e.stack.env,e.configuration).mml()),o&&l.appendChild(o),l}(e,i,n);return o}var d=t(679),f=t(489),g=t(196),h=function(e,n){var t=e.ParseArg(n);if(!g.Z.isInferred(t))return t;var r=g.Z.getChildren(t);if(1===r.length)return r[0];var i=e.create("node","mrow");return g.Z.copyChildren(t,i),g.Z.copyAttributes(t,i),i},b=t(564),v=t(569),x={real:Object.assign({},l.$4),imaginary:Object.assign({},l.$4),inputMode:"cartesian"};function y(e,n,t){var r=Object.assign({},x);if(n.includes(":")){r.inputMode="polar";var i=n.match(/(.+):(.+)/);r.real=(0,l.p3)(e,i[1],t),r.imaginary=(0,l.p3)(e,i[2],t)}else{var a=new RegExp("[".concat(t["input-complex-root"],"]"));if(n.match(a)){var o=/[+-](?![^+-]*[+-])/,s=n.match(o),u=n.split(o);if(u.length>1){r.real=(0,l.p3)(e,u[0].trim(),t);var c=u[1].replace(a,"").trim();c||(c="1"),r.imaginary=(0,l.p3)(e,s+c,t)}else{var p=u[0].replace(a,"");p||(p="1"),r.imaginary=(0,l.p3)(e,p,t)}}else r.real=(0,l.p3)(e,n,t)}return r}function M(e,n,t){var r=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],i=(0,l.Yc)(n.real),a=(0,l.Yc)(n.imaginary),o=Math.hypot(i,a),s=Math.atan2(a,i);r&&(s=s/2/Math.PI*360),n.real=(0,l.p3)(e,o.toString(),t),n.imaginary=(0,l.p3)(e,s.toString(),t)}function O(e,n,t){var r=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],i=(0,l.Yc)(n.real),a=(0,l.Yc)(n.imaginary);r&&(a=2*a*Math.PI/360);var o=i*Math.cos(a),s=i*Math.sin(a);n.real=(0,l.p3)(e,o.toString(),t),n.imaginary=(0,l.p3)(e,s.toString(),t)}function w(e,n,t){var r=(0,s.co)(e.real,n,t),i=n.create("node","inferredMrow",[],{});i.appendChild(r);var o=(0,l.Yc)(e.imaginary);if(0!==o)if("polar"===e.inputMode&&"input"===t["complex-mode"]||"polar"===t["complex-mode"]){var u=new a.Z(t["complex-symbol-angle"],n.stack.env,n.configuration).mml();i.appendChild(u);var c=(0,s.co)(e.imaginary,n,t);if(i.appendChild(c),"degrees"===t["complex-angle-unit"]){var p=new a.Z(t["complex-symbol-degree"],n.stack.env,n.configuration).mml();i.appendChild(p)}}else{var _="-"===e.imaginary.sign?"-":"+";if(e.imaginary.sign="",0!==(0,l.Yc)(e.real)||"-"===_){var m=new a.Z(_,n.stack.env,n.configuration).mml();i.appendChild(m)}if("before-number"===t["complex-root-position"]){var d=new a.Z(t["output-complex-root"],n.stack.env,n.configuration).mml();i.appendChild(d)}if(1!==o||t["print-complex-unity"]){var f=(0,s.co)(e.imaginary,n,t);i.appendChild(f)}if("after-number"===t["complex-root-position"]){var g=new a.Z(t["output-complex-root"],n.stack.env,n.configuration).mml();i.appendChild(g)}}return i}var E=t(286),P=t(599);var k=t(278);var C=t(460);t(106);function D(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}var A={"\\num":function(e){e.Push((0,l.tL)(e))},"\\ang":function(e){e.Push(m(e))},"\\unit":function(e){e.Push((0,f.PM)(e))},"\\qty":function(e){(0,d.Pm)(e)},"\\numlist":function(e){(0,E.e5)(e)},"\\qtylist":function(e){(0,k._)(e)},"\\numproduct":function(e){(0,P._)(e)},"\\qtyproduct":function(e){(0,C.E)(e)},"\\numrange":function(e){!function(e){var n=Object.assign({},e.options.siunitx),t=(0,u.b9)(e,n);Object.assign(n,t);var r=e.GetArgument("firstNum"),i=e.GetArgument("lastNum");if(n["parse-numbers"]){var o=(0,l.p3)(e,r,n),c=(0,l.p3)(e,i,n);if("individual"===n["range-exponents"])(0,v.V)(e,o,n),(0,v.V)(e,c,n);else{var p=o.exponentSign+o.exponent,_=Object.assign(n,{exponentMode:"fixed",fixedExponent:p});(0,v.V)(e,o,n),(0,v.V)(e,c,_)}var m=E.OH.get(n["range-exponents"])([o,c],e,n),d=(0,s.co)(m.numbers[0],e,n),f=new a.Z("\\text{".concat(n["range-phrase"],"}"),e.stack.env,e.configuration).mml(),g=(0,s.co)(m.numbers[1],e,n),h=[];m.leading&&h.push(m.leading),h=h.concat(d).concat(f).concat(g),m.trailing&&(h=h.concat(m.trailing)),h.forEach((function(n){e.Push(n)}))}else{var b=new a.Z(r+i,e.stack.env,e.configuration).mml();e.Push(b)}}(e)},"\\qtyrange":function(e){!function(e){var n=Object.assign({},e.options.siunitx),t=(0,u.b9)(e,n);Object.assign(n,t);var r=e.GetArgument("firstNum"),i=e.GetArgument("lastNum"),o=e.GetArgument("unit"),s=-1===o.indexOf("\\"),c=(0,f.n2)(e,o,n,t,s);if(n["parse-numbers"]){var p=(0,l.p3)(e,r,n),_=(0,l.p3)(e,i,n);if("individual"===n["range-exponents"])(0,v.V)(e,p,n),(0,v.V)(e,_,n);else{var m=p.exponentSign+p.exponent,g=Object.assign(n,{exponentMode:"fixed",fixedExponent:m});(0,v.V)(e,p,n),(0,v.V)(e,_,g)}var h=(0,f.Ho)(e,c,n,s),b=new a.Z(h,e.stack.env,e.configuration).mml(),x=(0,d.mD)(e,n);if(x){var y=e.create("node","inferredMrow",[],{});y.appendChild(x),y.appendChild(b),b=y}var M=E.OH.get(n["range-exponents"])([p,_],e,n),O=k.N.get(n["range-units"])(M,b,e,n),w=new a.Z("\\text{".concat(n["range-phrase"],"}"),e.stack.env,e.configuration).mml(),P=e.create("node","inferredMrow",[],{});M.leading&&P.appendChild(M.leading),P.appendChild(O.numbers[0]),P.appendChild(w),P.appendChild(O.numbers[1]),M.trailing&&P.appendChild(M.trailing),e.Push(P)}else{var C=new a.Z(r+i,e.stack.env,e.configuration).mml();e.Push(C)}}(e)},"\\complexnum":function(e){e.Push(function(e){var n=Object.assign({},e.options.siunitx),t=(0,u.b9)(e,n);Object.assign(n,t);var r=e.GetArgument("complexnum");if(n["parse-numbers"]){var i=y(e,r,n);return"polar"===n["complex-mode"]&&"polar"!==i.inputMode?M(e,i,n,"degrees"===n["complex-angle-unit"]):"cartesian"===n["complex-mode"]&&"cartesian"!==i.inputMode&&O(e,i,n,"degrees"===n["complex-angle-unit"]),(0,v.V)(e,i.real,n),(0,v.V)(e,i.imaginary,n),w(i,e,n)}return new a.Z(r,e.stack.env,e.configuration).mml()}(e))},"\\complexqty":function(e){!function(e){var n,t,r=Object.assign({},e.options.siunitx),i=(0,u.b9)(e,r);Object.assign(r,i);var o,l=e.GetArgument("complexnum"),c=e.GetArgument("unit"),p=-1===c.indexOf("\\"),_=(0,f.n2)(e,c,r,i,p),m=y(e,l,r);"polar"===r["complex-mode"]&&"polar"!==m.inputMode?M(e,m,r):"cartesian"===r["complex-mode"]&&"cartesian"!==m.inputMode&&O(e,m,r),null===(n=d.pk.get(r["prefix-mode"]))||void 0===n||n(e,m.real,_,r),null===(t=d.pk.get(r["prefix-mode"]))||void 0===t||t(e,m.imaginary,_,r),(0,v.V)(e,m.real,r),(0,v.V)(e,m.imaginary,r);var g=w(m,e,r);e.Push(g);var h=null,b=r["quantity-product"].trimStart();if(""!==b){var x=new a.Z(b,e.stack.env,e.configuration).mml(),E=(0,s.uI)(x);h=e.create("token","mo",{},E)}e.Push(h),o=(0,f.Ho)(e,_,r,p);var P=new a.Z(o,e.stack.env,e.configuration).mml();e.Push(P)}(e)},"@{}S":function(e){},"\\tablenum":function(e){},"\\sisetup":function(e){(0,u.hD)(e)},"\\DeclareSIUnit":function(e){var n=e.configuration.packageData.get("siunitx"),t=n[T],r=n[I],i=(0,u.b9)(e,u.Sk),a=e.GetArgument("DeclareSIUnit"),o=e.GetArgument("DeclareSIUnit");t.set(a,o),void 0!==i&&r.set(a,i)},"\\DeclareSIQualifier":function(e){},"\\DeclareSIPower":function(e){}},T="siunitxUnits",I="siunitxUnitOptions";new i.eJ("angchar-symbols",(function(e,n){var t=n.attributes||{};t.mathvariant=b.d.Variant.NORMAL,t.class="MathML-Unit";var r=e.create("token","mi"),i=e.create("token","mi",t,n.char),a=e.create("node","msup",[r,i]);e.Push(a)}),{degreeminute:["\u2032",{}],degreesecond:["\u2033",{}]}),new i.QQ("siunitxMap",{num:["siunitxToken","num"],ang:["siunitxToken","ang"],complexnum:["siunitxToken","complexnum"],unit:["siunitxToken","unit"],qty:["siunitxToken","qty"],complexqty:["siunitxToken","complexqty"],numlist:["siunitxToken","numlist"],numproduct:["siunitxToken","numproduct"],numrange:["siunitxToken","numrange"],qtylist:["siunitxToken","qtylist"],qtyrange:["siunitxToken","qtyrange"],qtyproduct:["siunitxToken","qtyproduct"],DeclareSIUnit:["siunitxToken","DeclareSIUnit"],sisetup:["siunitxToken","sisetup"],arialabel:["Arialabel","arialabel"],data:["Dataset","data"]},{siunitxToken:function(e,n){var t;null===(t=A[n])||void 0===t||t.call(A,e)},Arialabel:function(e,n){var t=e.GetArgument(n),r=h(e,n);g.Z.setAttribute(r,"aria-label",t),e.Push(r)},Dataset:function(e,n){var t=e.GetArgument(n),r=h(e,n),i=t.split("=");g.Z.setAttribute(r,"data-"+i[0],i[1]),e.Push(r)}});r.VK.create("siunitx",{handler:{macro:["angchar-symbols","siunitxMap"]},options:{siunitx:u.Sk},config:function(e,n){var t;n.parseOptions.packageData.set("siunitx",(D(t={},T,new Map),D(t,I,new Map),t))}})},489:function(e,n,t){t.d(n,{Ho:function(){return g},PM:function(){return b},n2:function(){return h}});var r=t(516),i=t(693),a=t(660),o=t(124),s=t(936),l=t(763);function u(e){return function(e){if(Array.isArray(e))return c(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,n){if(!e)return;if("string"==typeof e)return c(e,n);var t=Object.prototype.toString.call(e).slice(8,-1);"Object"===t&&e.constructor&&(t=e.constructor.name);if("Map"===t||"Set"===t)return Array.from(e);if("Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t))return c(e,n)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function c(e,n){(null==n||n>e.length)&&(n=e.length);for(var t=0,r=new Array(n);t2&&void 0!==arguments[2]&&arguments[2],i="";e.cancel&&(i+="\\cancel{"),e.highlight&&(i+="{\\color{".concat(e.highlight,"}")),i+=n["unit-font-command"]+"{",n["power-half-as-sqrt"]&&e.power&&.5===e.power?(i+="\\sqrt{\\class{MathML-Unit}{".concat(e.prefix).concat(e.symbol,"}}"),e.power=null):i+="\\class{MathML-Unit}{".concat(e.prefix).concat(e.symbol,"}"),e.qualifier&&(i+=null===(t=d.get(n["qualifier-mode"]))||void 0===t?void 0:t(e.qualifier,n["qualifier-phrase"])),i+="}";var a=void 0!==e.power&&null!==e.power?r?Math.abs(e.power*("denominator"===e.position?-1:1)):e.power*("denominator"===e.position?-1:1):r?Math.abs(1*("denominator"===e.position?-1:1)):1*("denominator"===e.position?-1:1);return null!=a&&1!==a&&(i+="^{"+a+"}"),e.cancel&&(i+="}"),e.highlight&&(i+="}"),{latex:i,superscriptPresent:1!==a}}function g(e,n,t,r){var i=!1,a="";""!==t["unit-color"]?(a+="{\\color{".concat(t["unit-color"],"}"),i=!0):""!==t.color&&(a+="{\\color{".concat(t.color,"}"),i=!0);var o=!1;if(n.length>=2&&1===n.filter((function(e){var n=null!==e.power&&void 0!==e.power?e.power*("denominator"===e.position?-1:1):1;return-1===Math.sign(n)})).length&&"single-symbol"===t["per-mode"]&&(o=!0),r){var s="",l=null;n.every((function(e){return"denominator"!==e.position||(l=e,!1)})),n.forEach((function(e){e===l&&(s+=" / ");var n=f(e,t);""!==s&&(s+=t["inter-unit-product"]),s+=n.latex})),a+=s}else{var u=0,c=0;if(n.forEach((function(e){"denominator"===e.position||null!==e.power&&void 0!==e.power&&e.power<0?c++:u++}),0),"fraction"===t["per-mode"]||"symbol"===t["per-mode"]||"repeated-symbol"===t["per-mode"]||o||"single-symbol"===t["per-mode"]&&1===c&&u>0){var p="",_="",m=!1;n.forEach((function(e){var n;"denominator"===e.position||null!==e.power&&void 0!==e.power&&e.power<0?(n=f(e,t,"fraction"===t["per-mode"]||"symbol"===t["per-mode"]||"repeated-symbol"===t["per-mode"]||"single-symbol"===t["per-mode"]||o),""!==_&&("repeated-symbol"===t["per-mode"]?(n.superscriptPresent&&(_+=t["per-symbol-script-correction"]),_+=t["per-symbol"]):_+=t["inter-unit-product"]),_+=n.latex):(n=f(e,t,"fraction"===t["per-mode"]||"symbol"===t["per-mode"]||"repeated-symbol"===t["per-mode"]||"single-symbol"===t["per-mode"]||o),m=n.superscriptPresent,""!==p&&(p+=t["inter-unit-product"]),p+=n.latex)})),""===p&&""!==_&&(p="1"),""!==_?(c>1&&"symbol"===t["per-mode"]&&t["bracket-unit-denominator"]&&(_="("+_+")"),"fraction"===t["per-mode"]?a+=t["fraction-command"]+"{"+p+"}{"+_+"}":"repeated-symbol"===t["per-mode"]||"symbol"===t["per-mode"]||o||"single-symbol"===t["per-mode"]?a+=p+(m?t["per-symbol-script-correction"]:"")+t["per-symbol"]+_:console.log("shouldn't be here")):a+=p}else{"power-positive-first"===t["per-mode"]&&(n=n.sort((function(e,n){var t=null!==e.power&&void 0!==e.power?e:1;"denominator"===e.position&&(t=-t);var r=null!==n.power&&void 0!==n.power?n:1;return"denominator"===n.position&&(r=-r),tr?-1:0})));var d="";n.forEach((function(e){var n=f(e,t);""!==d&&(d+=t["inter-unit-product"]),d+=n.latex})),a+=d}}return i&&(a+="}"),a}function h(e,n,t,a,o){var s=new Array;if(o)s.push.apply(s,u(function(e,n){var t=new Array,r=new i.Z(n,e.stack.env,e.configuration);r.i=0;var a={position:"numerator"},o=!1,s="";for(;r.ie.length)&&(n=e.length);for(var t=0,r=new Array(n);te.length)&&(n=e.length);for(var t=0,r=new Array(n);t).get('inputSigns')(char, num); - } else { - (func as Map).get('inputUncertaintySigns')(char, num); - } - } - } - } else { - let macro = char; - char = ''; - while (subParser.i < subParser.string.length && char != '\\' && char != ' ') { - char = subParser.string.charAt(subParser.i); - if (char != '\\' && char != ' ') { - macro += char; - } - subParser.i++; - } + token = subParser.GetNext(); + subParser.i++; // GetNext() does not advance position unless skipping whitespace - if (mapping.has(macro)) { - const func = mapping.get(macro); - if (typeof func == 'function') { - (mapping.get(macro) as CharNumFunction)(macro, num); - } else { - if (num.whole == '' && num.decimal == '') { - (func as Map).get('inputSigns')(macro, num); - } else { - (func as Map).get('inputUncertaintySigns')(macro, num); - } - } - } - } - } else { - if (ang.minutes == null) { + if (token === ';'){ + if (!ang.minutes) { ang.minutes = generateNumberPiece(); num = ang.minutes; - } else if (ang.seconds == null) { + } else if (!ang.seconds) { ang.seconds = generateNumberPiece(); num = ang.seconds; } else { - throw siunitxError.TooManySemicolonsInAngle; + throw siunitxError.ExtraSemicolon; } - } + } else { + if (token === '\\') { + token += subParser.GetCS(); + } + + try { + mapping.get(token)(token, num); + } catch { + throw siunitxError.InvalidAngArgument(subParser.string); + } + } } // copied directly from parseNumber, this can be applied to degrees only most likely? // TODO: This duplicates some code... clean up? - if (!options.retainExplicitDecimalMarker && ang.degrees.decimal != '' && ang.degrees.fractional == '') { + if (!options["retain-explicit-decimal-marker"] && ang.degrees.decimal !== '' && ang.degrees.fractional === '') { ang.degrees.decimal = ''; } - if (!options.retainExplicitPlus && ang.degrees.sign == '+') { + if (!options["retain-explicit-plus"] && ang.degrees.sign === '+') { ang.degrees.sign = ''; } - const value = +(ang.degrees.whole + (ang.degrees.decimal != '' ? '.' : '') + ang.degrees.fractional); - if (value == 0 && !options.retainNegativeZero && ang.degrees.sign == '-') { + const value = +(ang.degrees.whole + (ang.degrees.decimal !== '' ? '.' : '') + ang.degrees.fractional); + if (value === 0 && !options["retain-negative-zero"] && ang.degrees.sign === '-') { ang.degrees.sign = ''; } @@ -103,17 +77,17 @@ function parseAngle(parser: TexParser, text: string, options: IAngleOptions): IA } function convertToArc(ang: IAnglePiece): void { - if (ang.minutes != null || ang.seconds != null) { + if (ang.minutes !== null || ang.seconds !== null) { // already arc format return; } // This ignores exponents. - if (ang.degrees.decimal != '') { + if (ang.degrees.decimal !== '') { const firstFraction = +('0.' + ang.degrees.fractional); ang.degrees.fractional = ''; ang.degrees.decimal = ''; - if (firstFraction != 0) { + if (firstFraction !== 0) { const minutes = firstFraction * 60; ang.minutes = generateNumberPiece(); ang.minutes.whole = Math.floor(minutes).toString(); @@ -135,11 +109,11 @@ function convertToArc(ang: IAnglePiece): void { function convertToDecimal(ang: IAnglePiece): void { let value = 0; - if (ang.seconds != null) { + if (ang.seconds !== undefined && ang.seconds !== null) { value = +ang.seconds.whole / 60; ang.seconds = null; } - if (ang.minutes != null) { + if (ang.minutes !== undefined && ang.minutes !== null) { value = (+ang.minutes.whole + value) / 60; ang.minutes = null; } @@ -153,6 +127,31 @@ function convertToDecimal(ang: IAnglePiece): void { } } +function degreeOverDecimal(parser: TexParser, inputNode: MmlNode, symbolToUse: string, options: IOptions, accent: boolean): MmlNode | undefined { + let degreeNodeToAdd: MmlNode; + // decimal will be in first mn node + const mnNodes = inputNode.findNodes('mn'); + + if (mnNodes && mnNodes.length > 0) { + const numNode = mnNodes[0]; + const textNode = numNode.childNodes[0] as TextNode; + const split = textNode.getText().split(options["output-decimal-marker"]); + if (split.length > 1) { // does contain decimal + const replacementNode = parser.create('node', 'inferredMrow', [], {}); + replacementNode.appendChild(parser.create('token', 'mn', {}, split[0])); + const mover = parser.create('node', 'mover', [], {"accent": accent}); + replacementNode.appendChild(mover); + mover.appendChild(parser.create('token', 'mo', {}, '.')); + mover.appendChild((new TexParser('\\class{MathML-Unit}{\\mathrm{' + symbolToUse + '}}', parser.stack.env, parser.configuration)).mml()); + replacementNode.appendChild(parser.create('token', 'mn', {}, split[1])); + const parent = numNode.parent; + parent.replaceChild(replacementNode, numNode); + //console.log(JSON.parse(JSON.stringify(numNode))); + degreeNodeToAdd = inputNode as MmlNode; + } + } + return degreeNodeToAdd; +} const modeMapping = new Map void>([ // eslint-disable-next-line @typescript-eslint/no-empty-function @@ -161,134 +160,130 @@ const modeMapping = new Map void>([ ['decimal', convertToDecimal] ]); -function displayAngle(ang: IAnglePiece, options: IAngleOptions): string { - let displayResult = ''; - const degreeValue = +(ang.degrees.whole + (ang.degrees.decimal != '' ? '.' : '') + ang.degrees.fractional); - if (ang.degrees.whole == '' && options.fillAngleDegrees) { - if (ang.minutes.sign == '-') { +function displayAngleMml(parser: TexParser, ang: IAnglePiece, options: IAngleOptions): MmlNode { + const root = parser.create('node', 'inferredMrow', [], {}); + + const degreeValue = +(ang.degrees.whole + (ang.degrees.decimal !== '' ? '.' : '') + ang.degrees.fractional); + if (!ang.degrees.whole && options["fill-angle-degrees"]) { + if (ang.minutes.sign === '-') { ang.degrees.sign = '-'; ang.minutes.sign = ''; - } else if (ang.seconds.sign == '-') { + } else if (ang.seconds.sign === '-') { ang.degrees.sign = '-'; ang.seconds.sign = ''; } ang.degrees.whole = '0'; } - if (degreeValue != 0 || ang.degrees.whole == '0' || options.fillAngleDegrees) { - if (options.angleSymbolOverDecimal) { - const number = displayNumber(ang.degrees, options); - const split = number.split(options.outputDecimalMarker); - if (split.length > 1) { - displayResult += split[0]; - displayResult += '\\rlap{' + options.outputDecimalMarker + '}{\\class{MathML-Unit}{\\mathrm{' + options.angleSymbolDegree + '}}}'; - displayResult += split[1]; - } else { - displayResult += number; - displayResult += options.numberAngleProduct; - displayResult += '\\class{MathML-Unit}{\\mathrm{' + options.angleSymbolDegree + '}}'; - } - } else { - - displayResult += displayNumber(ang.degrees, options); - displayResult += options.numberAngleProduct; - displayResult += '\\mathrm{' + options.angleSymbolDegree + '}'; + let degreeNodeToAdd: MmlNode; + if (degreeValue !== 0 || ang.degrees.whole === '0' || options["fill-angle-degrees"]) { + const degreeMml = displayNumberMml(ang.degrees, parser, options as IOptions); + if (options["angle-symbol-over-decimal"]) { + // TODO: assume no exponents, maybe check for this and thow error + degreeNodeToAdd = degreeOverDecimal(parser, degreeMml, options["angle-symbol-degree"], options as IOptions, true); } - } + if (!degreeNodeToAdd) { + // do nothing but add symbol to end + degreeNodeToAdd = parser.create('node', 'inferredMrow', [], {}); + degreeNodeToAdd.appendChild(degreeMml); + degreeNodeToAdd.appendChild((new TexParser(options["number-angle-product"] + '\\class{MathML-Unit}{\\mathrm{' + options["angle-symbol-degree"] + '}}', parser.stack.env, parser.configuration)).mml()); - - if (displayResult != '' && options.angleSeparator != '') { - displayResult += options.angleSeparator; + } } - if (ang.minutes != null) { - const minutesValue = +(ang.minutes.whole + (ang.minutes.decimal != '' ? '.' : '') + ang.minutes.fractional); - let moddedAngleSymbolMinute = '\\mathrm{' + options.angleSymbolMinute + '}'; + let minuteNodeToAdd: MmlNode; + if (ang.minutes !== undefined && ang.minutes !== null) { + const minutesValue = +(ang.minutes.whole + (ang.minutes.decimal !== '' ? '.' : '') + ang.minutes.fractional); + let moddedAngleSymbolMinute = '\\mathrm{' + options["angle-symbol-minute"] + '}'; if (moddedAngleSymbolMinute === "\\mathrm{'}") { // TODO: Localize the degree-minutes - if (minutesValue == 1) + if (minutesValue === 1) moddedAngleSymbolMinute = '\\arialabel{degree-minute}{\\degreeminute}'; else moddedAngleSymbolMinute = '\\arialabel{degree-minutes}{\\degreeminute}'; } - if (minutesValue != 0 || ang.minutes.whole == '0' || options.fillAngleMinutes) { - if (minutesValue == 0 && options.fillAngleMinutes) { - if (ang.seconds.sign == '-') { + if (minutesValue !== 0 || ang.minutes.whole === '0' || options["fill-angle-minutes"]) { + + if (minutesValue === 0 && options["fill-angle-minutes"]) { + if (ang.seconds.sign === '-') { ang.minutes.sign = '-'; ang.seconds.sign = ''; } ang.minutes.whole = '0'; } + const minutesMml = displayNumberMml(ang.minutes, parser, options as IOptions); + if (options["angle-symbol-over-decimal"]) { + //const number = displayNumber(ang.minutes, options); + minuteNodeToAdd = degreeOverDecimal(parser, minutesMml, moddedAngleSymbolMinute, options as IOptions, false); + } - if (options.angleSymbolOverDecimal) { - const number = displayNumber(ang.minutes, options); - const split = number.split(options.outputDecimalMarker); - if (split.length > 1) { - displayResult += split[0]; - displayResult += '\\rlap{' + options.outputDecimalMarker + '}{' + moddedAngleSymbolMinute + '}'; - displayResult += split[1]; - } else { - displayResult += number; - displayResult += options.numberAngleProduct; - displayResult += moddedAngleSymbolMinute; - } - } else { - displayResult += displayNumber(ang.minutes, options); - displayResult += options.numberAngleProduct; - displayResult += moddedAngleSymbolMinute; + if (!minuteNodeToAdd) { + // do nothing but add symbol to end + minuteNodeToAdd = parser.create('node', 'inferredMrow', [], {}); + minuteNodeToAdd.appendChild(minutesMml); + minuteNodeToAdd.appendChild((new TexParser(options["number-angle-product"] + '\\class{MathML-Unit}{\\mathrm{' + moddedAngleSymbolMinute + '}}', parser.stack.env, parser.configuration)).mml()); } } } - if (displayResult != '' && options.angleSeparator != '' && !displayResult.endsWith(options.angleSeparator)) { - displayResult += options.angleSeparator; - } - if (ang.seconds != null) { - const secondsValue = +(ang.seconds.whole + (ang.seconds.decimal != '' ? '.' : '') + ang.seconds.fractional); - let moddedAngleSymbolSecond = '\\mathrm{' + options.angleSymbolSecond + '}'; + let secondsNodeToAdd: MmlNode; + if (ang.seconds !== undefined && ang.seconds !== null) { + const secondsValue = +(ang.seconds.whole + (ang.seconds.decimal !== '' ? '.' : '') + ang.seconds.fractional); + let moddedAngleSymbolSecond = '\\mathrm{' + options["angle-symbol-second"] + '}'; if (moddedAngleSymbolSecond === "\\mathrm{''}") { // TODO: Localize the degree-seconds - if (secondsValue == 1) + if (secondsValue === 1) moddedAngleSymbolSecond = '\\arialabel{degree-second}{\\degreesecond}'; else moddedAngleSymbolSecond = '\\arialabel{degree-seconds}{\\degreesecond}'; } - if (secondsValue != 0 || ang.seconds.whole == '0' || options.fillAngleSeconds) { - if (secondsValue == 0 && options.fillAngleSeconds) { + if (secondsValue !== 0 || ang.seconds.whole === '0' || options["fill-angle-seconds"]) { + if (secondsValue === 0 && options["fill-angle-seconds"]) { ang.seconds.whole = '0'; } - if (options.angleSymbolOverDecimal) { - const number = displayNumber(ang.seconds, options); - const split = number.split(options.outputDecimalMarker); - if (split.length > 1) { - displayResult += split[0]; - displayResult += '\\rlap{' + options.outputDecimalMarker + '}{' + moddedAngleSymbolSecond + '}'; - displayResult += split[1]; - } else { - displayResult += number; - displayResult += options.numberAngleProduct; - displayResult += moddedAngleSymbolSecond; - } - } else { - displayResult += displayNumber(ang.seconds, options); - displayResult += options.numberAngleProduct; - displayResult += moddedAngleSymbolSecond + const secondsMml = displayNumberMml(ang.seconds, parser, options as IOptions); + if (options["angle-symbol-over-decimal"]) { + //const number = displayNumberMml(ang.seconds, parser, options); + secondsNodeToAdd = degreeOverDecimal(parser, secondsMml, moddedAngleSymbolSecond, options as IOptions, false); + } + + if (!secondsNodeToAdd) { + // do nothing but add symbol to end + secondsNodeToAdd = parser.create('node', 'inferredMrow', [], {}); + secondsNodeToAdd.appendChild(secondsMml); + secondsNodeToAdd.appendChild((new TexParser(options["number-angle-product"] + '\\class{MathML-Unit}{\\mathrm{' + moddedAngleSymbolSecond + '}}', parser.stack.env, parser.configuration)).mml()); } } + } + + if (degreeNodeToAdd) { + root.appendChild(degreeNodeToAdd); + } + if (degreeNodeToAdd && (minuteNodeToAdd || secondsNodeToAdd) && options["angle-separator"] !== '') { + root.appendChild((new TexParser(options["angle-separator"], parser.stack.env, parser.configuration)).mml()); + } + if (minuteNodeToAdd){ + root.appendChild(minuteNodeToAdd); + } + if (minuteNodeToAdd && secondsNodeToAdd && options["angle-separator"] !== '') { + root.appendChild((new TexParser(options["angle-separator"], parser.stack.env, parser.configuration)).mml()); } - //console.log(displayResult); - return displayResult; + if (secondsNodeToAdd){ + root.appendChild(secondsNodeToAdd); + } + + return root; } export function processAngle(parser: TexParser): MmlNode { - const globalOptions: IOptions = { ...parser.options as IOptions }; + const globalOptions: IOptions = { ...parser.options.siunitx as IOptions }; - const localOptions = findOptions(parser); + const localOptions = findOptions(parser, globalOptions); //const options = processOptions(globalOptions, localOptions); //options.forEach((v, k) => globalOptions[k] = v); @@ -299,16 +294,12 @@ export function processAngle(parser: TexParser): MmlNode { // FIXME: processOption here twice in processAngle? //processOptions(globalOptions, localOptionString); const ang = parseAngle(parser, text, globalOptions); - // TODO: consider error checking result // Is there an exponent?? Throw an error... or ignore it? // transform angle format - modeMapping.get(globalOptions.angleMode)(ang); - - const displayResult = displayAngle(ang, globalOptions); - - const mml = (new TexParser(displayResult, parser.stack.env, parser.configuration)).mml(); + modeMapping.get(globalOptions["angle-mode"])(ang); + const mml = displayAngleMml( parser, ang, globalOptions); return mml; } \ No newline at end of file diff --git a/src/aria-label.ts b/src/aria-label.ts index df06ce1..d7e1eb3 100644 --- a/src/aria-label.ts +++ b/src/aria-label.ts @@ -16,15 +16,11 @@ * limitations under the License. */ - -import { Configuration } from 'mathjax-full/js/input/tex/Configuration'; import { ParseMethod } from 'mathjax-full/js/input/tex/Types'; import TexParser from 'mathjax-full/js/input/tex/TexParser'; -import { CommandMap } from 'mathjax-full/js/input/tex/SymbolMap'; import { MmlNode } from 'mathjax-full/js/core/MmlTree/MmlNode'; import NodeUtil from 'mathjax-full/js/input/tex/NodeUtil'; - /** * Parses the math argument of the above commands and returns it as single * node (in an mrow if necessary). The HTML attributes are then @@ -33,12 +29,12 @@ import NodeUtil from 'mathjax-full/js/input/tex/NodeUtil'; * @param {string} name The calling macro name. * @return {MmlNode} The math node. */ -export let GetArgumentMML = function (parser: TexParser, name: string): MmlNode { - let arg = parser.ParseArg(name); +export const GetArgumentMML = function (parser: TexParser, name: string): MmlNode { + const arg = parser.ParseArg(name); if (!NodeUtil.isInferred(arg)) { return arg; } - let children = NodeUtil.getChildren(arg); + const children = NodeUtil.getChildren(arg); if (children.length === 1) { return children[0]; } @@ -49,7 +45,7 @@ export let GetArgumentMML = function (parser: TexParser, name: string): MmlNode }; -let ArialabelMethods: Record = {}; +const ArialabelMethods: Record = {}; /** * Implements \arialabel{name}{math} @@ -57,7 +53,7 @@ let ArialabelMethods: Record = {}; * @param {string} name The TeX string */ ArialabelMethods.Arialabel = function (parser: TexParser, name: string) { - let thelabel = parser.GetArgument(name); + const thelabel = parser.GetArgument(name); const arg = GetArgumentMML(parser, name); NodeUtil.setAttribute(arg, 'arialabel', thelabel); parser.Push(arg); diff --git a/src/complexMethods.ts b/src/complexMethods.ts index 287f607..79499af 100644 --- a/src/complexMethods.ts +++ b/src/complexMethods.ts @@ -2,11 +2,9 @@ import TexParser from "mathjax-full/js/input/tex/TexParser"; import { INumberPiece, NumberPieceDefault, parseNumber, pieceToNumber } from "./numMethods"; import { MmlNode } from "mathjax-full/js/core/MmlTree/MmlNode"; import { IOptions, findOptions } from "./options/options"; -import { IComplexNumberOptions } from "./options/complexNumberOptions"; -import { GlobalParser } from "./siunitx"; + import { postProcessNumber } from "./numPostProcessMethods"; -import { displayOutputMml, findInnerText, spacerMap } from "./numDisplayMethods"; -import { OPTIONS } from "mathjax-full/js/util/Options"; +import { displayOutputMml, findInnerText } from "./numDisplayMethods"; import { displayUnits, parseUnit } from "./unitMethods"; import { prefixModeMap } from "./qtyMethods"; @@ -37,24 +35,24 @@ export function parseComplexNumber(parser: TexParser, text: string, options: IOp } else { // parse cartesian // check if imaginary part exists - const imaginaryTokenRegex = new RegExp(`[${options.inputComplexRoot}]`); + const imaginaryTokenRegex = new RegExp(`[${options["input-complex-root"]}]`); if (text.match(imaginaryTokenRegex)) { // identify splitter and split parts const regexSplit = /[+-](?![^+-]*[+-])/; const sign = text.match(regexSplit); const split = text.split(regexSplit); if (split.length > 1) { - complex.real = parseNumber(parser, split[0], options); + complex.real = parseNumber(parser, split[0].trim(), options); //remove imaginary token, add sign, and parse let value = split[1].replace(imaginaryTokenRegex, '').trim(); - if (value === '') { + if (!value) { value = '1'; } complex.imaginary = parseNumber(parser, sign + value, options); } else { // here because positive imaginary only number was used... i.e. 2i let value = split[0].replace(imaginaryTokenRegex, ''); - if (value === '') { + if (!value) { value = '1'; } complex.imaginary = parseNumber(parser, value, options); @@ -90,156 +88,144 @@ function polarToCartesian(parser: TexParser, complex: IComplex, options: IOption } const x = r * Math.cos(ang); const y = r * Math.sin(ang); - + complex.real = parseNumber(parser, x.toString(), options); complex.imaginary = parseNumber(parser, y.toString(), options); } -function displayComplexNumber(complex: IComplex, parser: TexParser, options: IOptions): MmlNode[]{ - const realMmlNodes = displayOutputMml(complex.real, parser, options); - - let rootNodes: MmlNode[] = []; - realMmlNodes.forEach(n => { - rootNodes.push(n); - }) - - const complexValue = pieceToNumber(complex.imaginary); - if (complexValue !== 0) { - if (complex.inputMode === 'polar' && options.complexMode === 'input' || options.complexMode === 'polar') { - const angle = (new TexParser(options.complexSymbolAngle, parser.stack.env, parser.configuration)).mml(); - rootNodes.push(angle); - - - const complexMmlNodes = displayOutputMml(complex.imaginary, parser, options); - complexMmlNodes.forEach(n => { - rootNodes.push(n); - }); - - if (options.complexAngleUnit === 'degrees'){ - const degree = (new TexParser(options.complexSymbolDegree, parser.stack.env, parser.configuration)).mml(); - rootNodes.push(degree); - } +function displayComplexNumber(complex: IComplex, parser: TexParser, options: IOptions): MmlNode { + const realMmlNode = displayOutputMml(complex.real, parser, options); - } else { - // extract sign from imaginary part - const sign = complex.imaginary.sign === '-' ? '-' : '+'; - complex.imaginary.sign = ''; + const root = parser.create('node', 'inferredMrow', [], {}); + root.appendChild(realMmlNode); - if (pieceToNumber(complex.real) !== 0 || sign === '-') { - const signNode = (new TexParser(sign, parser.stack.env, parser.configuration)).mml(); - rootNodes.push(signNode); - } + const complexValue = pieceToNumber(complex.imaginary); + if (complexValue !== 0) { + if (complex.inputMode === 'polar' && options["complex-mode"] === 'input' || options["complex-mode"] === 'polar') { + const angle = (new TexParser(options["complex-symbol-angle"], parser.stack.env, parser.configuration)).mml(); + root.appendChild(angle); - if (options.complexRootPosition === 'before-number') { - const rootNode = (new TexParser(options.outputComplexRoot, parser.stack.env, parser.configuration)).mml(); - rootNodes.push(rootNode); - } - if (complexValue !== 1 || options.printComplexUnity){ - const complexMmlNodes = displayOutputMml(complex.imaginary, parser, options); - complexMmlNodes.forEach(n => { - rootNodes.push(n); - }); - } + const complexMmlNode = displayOutputMml(complex.imaginary, parser, options); - if (options.complexRootPosition === 'after-number') { - const rootNode = (new TexParser(options.outputComplexRoot, parser.stack.env, parser.configuration)).mml(); - rootNodes.push(rootNode); - } + root.appendChild(complexMmlNode); + + if (options["complex-angle-unit"] === 'degrees') { + const degree = (new TexParser(options["complex-symbol-degree"], parser.stack.env, parser.configuration)).mml(); + root.appendChild(degree); + } + + } else { + // extract sign from imaginary part + const sign = complex.imaginary.sign === '-' ? '-' : '+'; + complex.imaginary.sign = ''; + + if (pieceToNumber(complex.real) !== 0 || sign === '-') { + const signNode = (new TexParser(sign, parser.stack.env, parser.configuration)).mml(); + root.appendChild(signNode); + } + + if (options["complex-root-position"] === 'before-number') { + const rootNode = (new TexParser(options["output-complex-root"], parser.stack.env, parser.configuration)).mml(); + root.appendChild(rootNode); + } + + if (complexValue !== 1 || options["print-complex-unity"]) { + const complexMmlNode = displayOutputMml(complex.imaginary, parser, options); + root.appendChild(complexMmlNode); + } + + if (options["complex-root-position"] === 'after-number') { + const rootNode = (new TexParser(options["output-complex-root"], parser.stack.env, parser.configuration)).mml(); + root.appendChild(rootNode); } } + } - return rootNodes; + return root; } -export function processComplexNumber(parser: TexParser): MmlNode[] { - const globalOptions: IOptions = { ...parser.options as IOptions }; +export function processComplexNumber(parser: TexParser): MmlNode { + const globalOptions: IOptions = { ...parser.options.siunitx as IOptions }; - const localOptions = findOptions(parser); + const localOptions = findOptions(parser, globalOptions); Object.assign(globalOptions, localOptions); - let text = parser.GetArgument('complexnum'); + const text = parser.GetArgument('complexnum'); - if (globalOptions.parseNumbers) { + if (globalOptions["parse-numbers"]) { const complex = parseComplexNumber(parser, text, globalOptions); - if (globalOptions.complexMode === 'polar' && complex.inputMode !== 'polar') { - cartesianToPolar(parser, complex, globalOptions, globalOptions.complexAngleUnit === 'degrees'); - } else if (globalOptions.complexMode === 'cartesian' && complex.inputMode !== 'cartesian') { - polarToCartesian(parser, complex, globalOptions, globalOptions.complexAngleUnit === 'degrees'); + if (globalOptions["complex-mode"] === 'polar' && complex.inputMode !== 'polar') { + cartesianToPolar(parser, complex, globalOptions, globalOptions["complex-angle-unit"] === 'degrees'); + } else if (globalOptions["complex-mode"] === 'cartesian' && complex.inputMode !== 'cartesian') { + polarToCartesian(parser, complex, globalOptions, globalOptions["complex-angle-unit"] === 'degrees'); } - postProcessNumber(complex.real, globalOptions); - postProcessNumber(complex.imaginary, globalOptions); + postProcessNumber(parser, complex.real, globalOptions); + postProcessNumber(parser, complex.imaginary, globalOptions); - return displayComplexNumber(complex, parser, globalOptions); + return displayComplexNumber(complex, parser, globalOptions); } else { const mml = (new TexParser(text, parser.stack.env, parser.configuration)).mml(); - return [mml]; + return mml; } } export function processComplexQuantity(parser: TexParser): void { - const globalOptions: IOptions = { ...parser.options as IOptions }; + const globalOptions: IOptions = { ...parser.options.siunitx as IOptions }; - const localOptions = findOptions(parser); + const localOptions = findOptions(parser, globalOptions); Object.assign(globalOptions, localOptions); - let complexnum = parser.GetArgument('complexnum'); + const complexnum = parser.GetArgument('complexnum'); const unitString = parser.GetArgument('unit'); let unitDisplay = ''; - const isLiteral = (unitString.indexOf('\\') == -1); - const unitPieces = parseUnit(parser, unitString, globalOptions, localOptions, isLiteral); + const isLiteral = (unitString.indexOf('\\') === -1); + const unitPieces = parseUnit(parser, unitString, globalOptions, localOptions, isLiteral); + - const complex = parseComplexNumber(parser, complexnum, globalOptions); - if (globalOptions.complexMode === 'polar' && complex.inputMode !== 'polar') { + if (globalOptions["complex-mode"] === 'polar' && complex.inputMode !== 'polar') { cartesianToPolar(parser, complex, globalOptions); - } else if (globalOptions.complexMode === 'cartesian' && complex.inputMode !== 'cartesian') { + } else if (globalOptions["complex-mode"] === 'cartesian' && complex.inputMode !== 'cartesian') { polarToCartesian(parser, complex, globalOptions); } // convert number and unit if necessary - prefixModeMap.get(globalOptions.prefixMode)?.(complex.real, unitPieces, globalOptions); - prefixModeMap.get(globalOptions.prefixMode)?.(complex.imaginary, unitPieces, globalOptions); + prefixModeMap.get(globalOptions["prefix-mode"])?.(parser, complex.real, unitPieces, globalOptions); + prefixModeMap.get(globalOptions["prefix-mode"])?.(parser, complex.imaginary, unitPieces, globalOptions); - postProcessNumber(complex.real, globalOptions); - postProcessNumber(complex.imaginary, globalOptions); + postProcessNumber(parser, complex.real, globalOptions); + postProcessNumber(parser, complex.imaginary, globalOptions); - - const complexNumMml = displayComplexNumber( complex, parser, globalOptions); - complexNumMml.forEach(v=>{ - parser.Push(v); - }); + + const complexNumMml = displayComplexNumber(complex, parser, globalOptions); + parser.Push(complexNumMml); let quantityProductNode = null; - const trimmedQuantityProduct = globalOptions.quantityProduct.trimStart(); + const trimmedQuantityProduct = globalOptions["quantity-product"].trimStart(); if (trimmedQuantityProduct !== '') { - let quantityProduct = spacerMap[trimmedQuantityProduct]; - if (quantityProduct === undefined) { - // instead of copying quantityProduct, - // should auto parse latex and extract unicode from mml - const spacerNode = (new TexParser(quantityProduct, GlobalParser.stack.env, GlobalParser.configuration)).mml(); - quantityProduct = findInnerText(spacerNode); - } - quantityProductNode = parser.create('token', 'mo', {}, quantityProduct); + const spacerNode = (new TexParser(trimmedQuantityProduct, parser.stack.env, parser.configuration)).mml(); + const spacerUnicode = findInnerText(spacerNode); + quantityProductNode = parser.create('token', 'mo', {}, spacerUnicode); } parser.Push(quantityProductNode); - // Need to process this after number because some options alter unit prefixes + // Need to process this after number because some options alter unit prefixes unitDisplay = displayUnits(parser, unitPieces, globalOptions, isLiteral); const unitNode = (new TexParser(unitDisplay, parser.stack.env, parser.configuration)).mml(); parser.Push(unitNode); - + } diff --git a/src/error/errors.ts b/src/error/errors.ts new file mode 100644 index 0000000..43f48b3 --- /dev/null +++ b/src/error/errors.ts @@ -0,0 +1,28 @@ +import TexError from "mathjax-full/js/input/tex/TexError"; +import * as data from "./resource.en.json"; + +export class siunitxError { + + static BadOptionChars = (option: string) => new TexError('siunitx:badOptionChars', data.badOptionsChars, option); + + static ComparatorAlreadySet = (existing: string, toBeAdded: string) => new TexError('siunitx:comparatorAlreadySet', data.comparatorAlreadySet, existing, toBeAdded); + + static ExponentThresholdsError = (option: string) => new TexError('sinunitx:exponentThresholdsError', data.exponentThresholdsError, option); + + static ExtraSemicolon = () => new TexError('siunitx:extraSemicolon', data.extraSemicolon); + + static InvalidAngArgument = (arg: string) => new TexError('siunitx:invalidAngArgument', data.invalidAngArgument, arg) + + static InvalidNumArgument = (arg: string) => new TexError('siunitx:invalidNumArgument', data.invalidNumArgument, arg); + + static InvalidOptionValue = (key: string, type:string) => new TexError('siunitx:invalidOptionValue', data.invalidOptionValue, key, type) + + static LiteralUnitsForbidden = (macro: string) => new TexError('siunitx:literalUnitsForbidden', data.literalUnitsForbidden, macro); + + static NoInterpretationForUnitMacro = (macro: string) => new TexError('siunitx:macroNotDefined', data.macroNotDefined, macro); + + static NoUncertaintyToClose = () => new TexError('siunitx:noUncertaintyToClose', data.noUncertaintyToClose); + + static UncertaintyAlreadyClosed = () => new TexError('siunitx:uncertaintyAlreadyClosed', data.uncertaintyAlreadyClosed); + +} diff --git a/src/error/resource.en.json b/src/error/resource.en.json new file mode 100644 index 0000000..8fa56b3 --- /dev/null +++ b/src/error/resource.en.json @@ -0,0 +1,13 @@ +{ + "badOptionsChars": "Invalid control sequence at the end of the %1 option.", + "comparatorAlreadySet": "A comparator has already been set. %1 is already present, but you're trying to add %2.", + "exponentThresholdsError": "Couldn't parse %1. Option \"exponent-threshold\" must be in the form of \"min-number:max-number\".", + "extraSemicolon": "There are two many semi-colons. Should only be two at most.", + "invalidAngArgument": "%1 contains invalid characters and cannot be parsed by siunitx for the ang macro.", + "invalidNumArgument": "%1 contains invalid characters and cannot be parsed by siunitx for the num macro.", + "invalidOptionValue": "Key %1 accepts only values of type %2.", + "literalUnitsForbidden": "%1 is detected as a literal unit. These are forbidden globally. Use interpreted units instead (i.e. \\kilo\\gram).", + "macroNotDefined": "The unit macro, %1, has not been defined.", + "noUncertaintyToClose": "Trying to close an uncertainty that doesn't exist.", + "uncertaintyAlreadyClosed": "Uncertainty was already closed." +} \ No newline at end of file diff --git a/src/errors.ts b/src/errors.ts deleted file mode 100644 index d866a85..0000000 --- a/src/errors.ts +++ /dev/null @@ -1,34 +0,0 @@ -import TexError from "mathjax-full/js/input/tex/TexError"; - -// interface IError { -// code: string; -// message: string; -// } - -// class Error implements IError{ -// code: string; -// message: string; -// constructor(code:string, message:string){ -// this.code = code; -// this.message = message; -// } -// } - -export class siunitxError { - - - static TooManySemicolonsInAngle = new TexError('101', 'There are two many semi-colons. Should only be two at most.'); - - static NoInterpretationForUnitMacro(macro:string) : TexError { - return new TexError('102', 'The unit macro, ' + macro + ', has not been defined.'); - } - - static LiteralUnitsForbidden(macro:string) : TexError { - return new TexError('103', macro + ' is detected as a literal units. These are forbidden globally. Use interpreted units instead (i.e. \\kilo\\gram).'); - } - - static ExponentThresholdsError(option:string):TexError{ - return new TexError('104', 'Couldn\'t parse "' + option + '". Option "exponent-threshold" must be in the form of "min-number:max-number".'); - } - -} diff --git a/src/mathmlcompare.ts b/src/mathmlcompare.ts index 4c80a50..d805680 100644 --- a/src/mathmlcompare.ts +++ b/src/mathmlcompare.ts @@ -17,8 +17,8 @@ export function mathmlcompare(mml1: string, mml2: string) { .replace(/(>)\s+|\s+(<)/g, "$1") // Trim content in between tags .replace(/<([a-zA-Z]+)><\/\1>/g, "<$1/>"); // Convert empty tags into self closing tags - }; + } - return simplifyMML(mml1) == simplifyMML(mml2); + return simplifyMML(mml1) === simplifyMML(mml2); -}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/numDisplayMethods.ts b/src/numDisplayMethods.ts index 3e6a2dc..20406ab 100644 --- a/src/numDisplayMethods.ts +++ b/src/numDisplayMethods.ts @@ -2,76 +2,79 @@ import TexParser from "mathjax-full/js/input/tex/TexParser"; import { INumberPiece, IUncertainty } from "./numMethods"; import { IOptions } from "./options/options"; import { INumOutputOptions } from "./options/numberOptions"; -import { MmlNode, TextNode } from "mathjax-full/js/core/MmlTree/MmlNode"; -import { GlobalParser } from "./siunitx"; -import NodeUtil from "mathjax-full/js/input/tex/NodeUtil"; - -export const spacerMap: Record = { - '\\,': '\u2009', // \, 3/18 quad - '\\ ': ' ', // space - '\\quad': '\u2001', // em quad - '\\qquad': '\u2001\u2001', // double em quad - '\\:': '\u2005', // \: is actually bigger than \, 4/18 quad - '\\;': '\u2004' // \; is actually bigger than \: 5/18 quad +import { AbstractMmlTokenNode, MmlNode } from "mathjax-full/js/core/MmlTree/MmlNode"; + +const spaceMap: Record = { + '1em': '\u2001', + '2em': '\u2001\u2001', + '0.167em': '\u2006', + '0.222em': '\u2005', // U+2005 is actually .25em + '0.278em': '\u2004', // U+2004 is actually .333em + // include no leading zero versions just in case... + '.167em': '\u2006', + '.222em': '\u2005', // U+2005 is actually .25em + '.278em': '\u2004', // U+2004 is actually .333em + //U+2009 is font dependent, so not reliable }; -// Naive function that assumes there is only one child for each node with children export function findInnerText(node: MmlNode): string { - let inner = node; - while (!inner.isToken && inner.childNodes.length > 0) { - inner = inner.childNodes[0] as MmlNode; - } - if (inner.isToken) { - return NodeUtil.getText(inner as TextNode); - } else { - return ""; - } -} - -function addSpacing(text: string, digitGroupSize: number, minimum: number, spacer: string, reverse: boolean, digitGroupFirstSize?: number, digitGroupOtherSize?: number) { - let mmlSpacer = spacerMap[spacer.trimStart()]; - if (mmlSpacer === undefined) { - // instead of copying spacer, - // should auto parse latex and extract unicode from mml - const spacerNode = (new TexParser(spacer, GlobalParser.stack.env, GlobalParser.configuration)).mml(); - mmlSpacer = findInnerText(spacerNode); - } - - if (text.length >= minimum) { - const arr = text.split(''); - let adjusted = 0; - const firstCount = (digitGroupFirstSize != -1 && digitGroupFirstSize != undefined) ? digitGroupFirstSize : digitGroupSize; - let fluidCount = firstCount; - if (reverse) { - for (let i = firstCount; i < arr.length; i += fluidCount) { - text = text.slice(0, i + adjusted) + mmlSpacer + text.slice(i + adjusted, text.length + adjusted); - adjusted += mmlSpacer.length; - fluidCount = (digitGroupOtherSize != -1 && digitGroupOtherSize != undefined) ? digitGroupOtherSize : digitGroupSize; + const data = { text: '' }; + node.walkTree((node: MmlNode, data: { text: string }) => { + if (!node.isToken) return; + if (node.isKind('mspace')) { + + const w = node.attributes.getExplicit('width'); + if (Object.prototype.hasOwnProperty.call(spaceMap, w)) { + data.text += spaceMap[w as string]; } } else { - for (let i = arr.length - firstCount; i >= 0; i -= fluidCount) { - text = text.slice(0, i) + mmlSpacer + text.slice(i, text.length + adjusted); - adjusted += mmlSpacer.length; - fluidCount = (digitGroupOtherSize != -1 && digitGroupOtherSize != undefined) ? digitGroupOtherSize : digitGroupSize; - } + data.text += (node as AbstractMmlTokenNode).getText(); + } + + }, data); + return data.text; +} + +function addSpacing( + parser: TexParser, + text: string, + digitGroupSize: number, + minimum: number, + spacer: string, + reverse: boolean, + digitGroupFirstSize: number, + digitGroupOtherSize: number +) { + // parse the latex space, extract the em spacing, and lookup the proper unicode space + const spacerNode = (new TexParser(spacer, parser.stack.env, parser.configuration)).mml(); + const mmlSpacer = findInnerText(spacerNode); + + // do not use text.split('') as that won't work with 32-bit unicode characters. + const digits = [...text]; + if (digits.length >= minimum) { + const firstSize = (digitGroupFirstSize !== -1 ? digitGroupFirstSize : digitGroupSize); + const groupSize = (digitGroupOtherSize !== -1 ? digitGroupOtherSize : digitGroupSize); + const [start, size] = (reverse ? [firstSize, groupSize + 1] : [digits.length - firstSize, -groupSize]); + for (let i = start; i >= 0 && i < digits.length; i += size) { + digits.splice(i, 0, mmlSpacer); } } - return text; + return digits.join(''); } -const groupNumbersMap = new Map void>([ - ['all', (num: INumberPiece, options: INumOutputOptions): void => { - num.whole = addSpacing(num.whole, options.digitGroupSize, options.groupMinimumDigits, options.groupSeparator, false, options.digitGroupFirstSize, options.digitGroupOtherSize); - num.fractional = addSpacing(num.fractional, options.digitGroupSize, options.groupMinimumDigits, options.groupSeparator, true, options.digitGroupFirstSize, options.digitGroupOtherSize); +const groupNumbersMap = new Map void>([ + ['all', (parser: TexParser, num: INumberPiece, options: INumOutputOptions): void => { + num.whole = addSpacing(parser, num.whole, options["digit-group-size"], options["group-minimum-digits"], options["group-separator"], false, options["digit-group-first-size"], options["digit-group-other-size"]); + num.fractional = addSpacing(parser, num.fractional, options["digit-group-size"], options["group-minimum-digits"], options["group-separator"], true, options["digit-group-first-size"], options["digit-group-other-size"]); }], - ['decimal', (num: INumberPiece, options: INumOutputOptions): void => { - num.fractional = addSpacing(num.fractional, options.digitGroupSize, options.groupMinimumDigits, options.groupSeparator, true, options.digitGroupFirstSize, options.digitGroupOtherSize); + ['decimal', (parser: TexParser, num: INumberPiece, options: INumOutputOptions): void => { + num.fractional = addSpacing(parser, num.fractional, options["digit-group-size"], options["group-minimum-digits"], options["group-separator"], true, options["digit-group-first-size"], options["digit-group-other-size"]); }], - ['integer', (num: INumberPiece, options: INumOutputOptions): void => { - num.whole = addSpacing(num.whole, options.digitGroupSize, options.groupMinimumDigits, options.groupSeparator, false, options.digitGroupFirstSize, options.digitGroupOtherSize); + ['integer', (parser: TexParser, num: INumberPiece, options: INumOutputOptions): void => { + num.whole = addSpacing(parser, num.whole, options["digit-group-size"], options["group-minimum-digits"], options["group-separator"], false, options["digit-group-first-size"], options["digit-group-other-size"]); }], // eslint-disable-next-line @typescript-eslint/no-empty-function ['none', (): void => { }] @@ -79,9 +82,9 @@ const groupNumbersMap = new Map= 0) { @@ -92,14 +95,14 @@ function convertUncertaintyToPlusMinus(uncertainty: IUncertainty, piece: INumber } uncertainty.fractional += uncertainty.whole; uncertainty.whole = '0'; - uncertainty.decimal = options.outputDecimalMarker; + uncertainty.decimal = options["output-decimal-marker"]; } else { // uncertainty is bigger than fraction. Need to split uncertainty to whole and fractional. // i.e. 123.4(12) ==> 123.4 +- 1.2 (diff = -1) // i.e. 123.4(144) ==> 123.4 +- 14.4 (diff = -2) // i.e. 123.45(144) ==> 123.45 +- 1.44 (diff = -1) uncertainty.fractional = uncertainty.whole.slice(Math.abs(diff)); - uncertainty.decimal = options.outputDecimalMarker; + uncertainty.decimal = options["output-decimal-marker"]; uncertainty.whole = uncertainty.whole.slice(0, Math.abs(diff)); } @@ -109,21 +112,21 @@ function convertUncertaintyToPlusMinus(uncertainty: IUncertainty, piece: INumber } export function convertUncertaintyToBracket(uncertainty: IUncertainty, piece: INumberPiece, options: INumOutputOptions): void { - if (uncertainty.type == 'bracket') { + if (uncertainty.type === 'bracket') { // check to make sure that uncertainty doesn't need a decimal point via 'compact marker' or 'full' (full adds zeros to fractional only uncertainty!) // should only be checked if there is NOT already a decimal point - if (uncertainty.decimal === '' && (options.uncertaintyMode === 'compact-marker' || options.uncertaintyMode === 'full')) { + if (!uncertainty.decimal && (options["uncertainty-mode"] === 'compact-marker' || options["uncertainty-mode"] === 'full')) { const diff = uncertainty.whole.length - piece.fractional.length; if (diff > 0) { uncertainty.fractional = uncertainty.whole.slice(diff, uncertainty.whole.length); uncertainty.whole = uncertainty.whole.slice(0, diff); if (uncertainty.fractional !== '') { - uncertainty.decimal = options.outputDecimalMarker; + uncertainty.decimal = options["output-decimal-marker"]; } - } else if (diff < 0 && options.uncertaintyMode === 'full') { + } else if (diff < 0 && options["uncertainty-mode"] === 'full') { uncertainty.fractional = ''.padEnd(Math.abs(diff), '0') + uncertainty.whole; uncertainty.whole = '0'; - uncertainty.decimal = options.outputDecimalMarker; + uncertainty.decimal = options["output-decimal-marker"]; } } @@ -135,7 +138,7 @@ export function convertUncertaintyToBracket(uncertainty: IUncertainty, piece: IN if (num < 1) { let position = 0; for (let i = 0; i < uncertainty.fractional.length; i++) { - if (uncertainty.fractional[i] != '0') { + if (uncertainty.fractional[i] !== '0') { break; } position++; @@ -147,35 +150,19 @@ export function convertUncertaintyToBracket(uncertainty: IUncertainty, piece: IN } } -// Deprecated -function displayUncertaintyBracket(uncertainty: IUncertainty, options: INumOutputOptions): string { - let output = options.uncertaintySeparator; - output += options.outputOpenUncertainty; - output += uncertainty.whole; - output += (options.uncertaintyMode === 'compact-marker' || options.uncertaintyMode === 'full') && uncertainty.decimal != '' ? options.outputDecimalMarker : ''; - output += uncertainty.fractional; - output += options.outputCloseUncertainty; - return output; -} - function displayUncertaintyBracketMml(uncertainty: IUncertainty, parser: TexParser, options: INumOutputOptions): MmlNode { - const uncertaintySeparator = (new TexParser(options.uncertaintySeparator, GlobalParser.stack.env, GlobalParser.configuration)).mml(); - const openUncertainty = (new TexParser(options.outputOpenUncertainty, GlobalParser.stack.env, GlobalParser.configuration)).mml(); + const uncertaintySeparator = (new TexParser(options["uncertainty-separator"], parser.stack.env, parser.configuration)).mml(); + const openUncertainty = (new TexParser(options["output-open-uncertainty"], parser.stack.env, parser.configuration)).mml(); let number = uncertainty.whole; - number += (options.uncertaintyMode === 'compact-marker' || options.uncertaintyMode === 'full') && uncertainty.decimal != '' ? options.outputDecimalMarker : ''; + number += (options["uncertainty-mode"] === 'compact-marker' || options["uncertainty-mode"] === 'full') && uncertainty.decimal !== '' ? options["output-decimal-marker"] : ''; number += uncertainty.fractional; const numberNode = parser.create('token', 'mn', {}, number); - const closeUncertainty = (new TexParser(options.outputCloseUncertainty, GlobalParser.stack.env, GlobalParser.configuration)).mml(); + const closeUncertainty = (new TexParser(options["output-close-uncertainty"], parser.stack.env, parser.configuration)).mml(); const mrow = parser.create('node', 'mrow', [uncertaintySeparator, openUncertainty, numberNode, closeUncertainty]); return mrow; } -// Deprecated -function displayUncertaintyPlusMinus(uncertainty: IUncertainty, options: INumOutputOptions): string { - return '\\pm' + displayNumber(uncertainty, options); -} - function displayUncertaintyPlusMinusMml(uncertainty: IUncertainty, parser: TexParser, options: INumOutputOptions): MmlNode { const numberNode = displayNumberMml(uncertainty, parser, options as IOptions); const plusMinusNode = parser.create('token', 'mo', {}, '\u00b1'); // plus-minus sign @@ -183,26 +170,6 @@ function displayUncertaintyPlusMinusMml(uncertainty: IUncertainty, parser: TexPa return mrow; } -// Deprecated -const uncertaintyModeMapping = new Map string>([ - ['separate', (uncertainty: IUncertainty, value: INumberPiece, options: INumOutputOptions): string => { - convertUncertaintyToPlusMinus(uncertainty, value, options); - return displayUncertaintyPlusMinus(uncertainty, options); - }], - ['compact', (uncertainty: IUncertainty, value: INumberPiece, options: INumOutputOptions): string => { - convertUncertaintyToBracket(uncertainty, value, options); - return displayUncertaintyBracket(uncertainty, options); - }], - ['full', (uncertainty: IUncertainty, value: INumberPiece, options: INumOutputOptions): string => { - convertUncertaintyToBracket(uncertainty, value, options); - return displayUncertaintyBracket(uncertainty, options); - }], - ['compact-marker', (uncertainty: IUncertainty, value: INumberPiece, options: INumOutputOptions): string => { - convertUncertaintyToBracket(uncertainty, value, options); - return displayUncertaintyBracket(uncertainty, options); - }], -]) - const uncertaintyModeMmlMapping = new Map MmlNode>([ ['separate', (uncertainty: IUncertainty, value: INumberPiece, parser: TexParser, options: INumOutputOptions): MmlNode => { convertUncertaintyToPlusMinus(uncertainty, value, options); @@ -222,138 +189,32 @@ const uncertaintyModeMmlMapping = new Map { - output += uncertaintyModeMapping.get(options.uncertaintyMode)?.(v, piece, options); - }); - - if (options.printZeroExponent && (piece.exponent == '' || (piece.exponent == '0'))) { - if (options.outputExponentMarker != '') { - output += options.outputExponentMarker; - output += '0'; - } else { - if (options.tightSpacing) { - output += '{' + options.exponentProduct + '}'; - } else { - output += options.exponentProduct; - } - output += options.exponentBase; - output += '^{0}'; - } - } else if (piece.exponent != '' && piece.exponent != '0') { - // if unity mantissa AND don't print it, then can't print exponentMarkers (E) nor exponentProduct (\\times) - if (piece.whole == '1' && piece.fractional == '' && !options.printUnityMantissa) { - output += options.exponentBase; - output += '^{' + piece.exponentSign + piece.exponent + '}'; - } else { - if (piece.exponentMarker != '') { - if (options.outputExponentMarker != '') { - output += options.outputExponentMarker; - output += piece.exponentSign + piece.exponent; - } else { - if (options.tightSpacing) { - output += (piece.whole != '' || piece.fractional != '') ? '{' + options.exponentProduct + '}' : ''; - } else { - output += (piece.whole != '' || piece.fractional != '') ? options.exponentProduct : ''; - } - output += options.exponentBase; - output += '^{' + piece.exponentSign + piece.exponent + '}'; - } - } - } - } - - if (options.bracketNegativeNumbers) { - if (piece.sign == '-') { - output += ')'; - } - } - if (options.negativeColor != '') { - output += '}'; - } - - //const mml = (new TexParser(output, GlobalParser.stack.env, GlobalParser.configuration)).mml(); - // resume hack: find zero symbol replacement - - return output; -} - -export function createExponentMml(num:INumberPiece, parser: TexParser, options: IOptions):MmlNode[]{ - const nodes = []; - const exponentProductNode = (new TexParser(options.exponentProduct, GlobalParser.stack.env, GlobalParser.configuration)).mml(); - const exponentBaseNode = (new TexParser(options.exponentBase, GlobalParser.stack.env, GlobalParser.configuration)).mml(); - if (options.printZeroExponent && (num.exponent == '' || (num.exponent == '0'))) { - const zeroNode = parser.create('token', 'mn', {}, '0'); - if (options.outputExponentMarker != '') { - const customExponentMarker = parser.create('token', 'mi', { mathvariant: 'normal' }, options.outputExponentMarker); - nodes.push(customExponentMarker); - nodes.push(zeroNode); - //currentNode.appendChild(customExponentMarker); - //currentNode.appendChild(zeroNode); - } else { - if (options.tightSpacing) { + if (options["tight-spacing"]) { exponentProductNode.attributes.set('lspace', '0em'); exponentProductNode.attributes.set('rspace', '0em'); } const exponential = parser.create('node', 'msup', [exponentBaseNode, zeroNode]); - nodes.push(exponentProductNode); - nodes.push(exponential); - // currentNode.appendChild(exponentProductNode); - // currentNode.appendChild(exponential); + // Edge case \num[print-unity-mantissa = false, print-zero-exponent = true]{1e0} + // as this should resolve to 10^0 without mantissa and exponent marker + if (options["print-unity-mantissa"]) { + root.appendChild(exponentProductNode); + } + root.appendChild(exponential); } - } else if (num.exponent != '' && num.exponent != '0') { + } else if (num.exponent !== '' && num.exponent !== '0') { const exponentSignNode = parser.create('token', 'mo', {}, num.exponentSign); const exponentValueNode = parser.create('token', 'mn', {}, num.exponent); const supPart = num.exponentSign === '-' @@ -361,222 +222,101 @@ export function createExponentMml(num:INumberPiece, parser: TexParser, options: : exponentValueNode; const exponential = parser.create('node', 'msup', [exponentBaseNode, supPart]); // if unity mantissa AND don't print it, then can't print exponentMarkers (E) nor exponentProduct (\\times) - if (num.whole == '1' && num.fractional == '' && !options.printUnityMantissa) { - //currentNode.appendChild(exponential); - nodes.push(exponential); + if (num.whole === '1' && num.fractional === '' && !options["print-unity-mantissa"]) { + root.appendChild(exponential); } else { - if (num.exponentMarker != '') { - if (options.outputExponentMarker != '') { - const customExponentMarker = (new TexParser(options.outputExponentMarker, GlobalParser.stack.env, GlobalParser.configuration)).mml(); - //const customExponentMarker = parser.create('token', 'mi', { }, options.outputExponentMarker); - nodes.push(customExponentMarker); - nodes.push(supPart); - // currentNode.appendChild(customExponentMarker); - // currentNode.appendChild(supPart); + if (num.exponentMarker !== '') { + if (options["output-exponent-marker"] !== '') { + const customExponentMarker = (new TexParser(options["output-exponent-marker"], parser.stack.env, parser.configuration)).mml(); + root.appendChild(customExponentMarker); + root.appendChild(supPart); } else { - if (num.whole != '' || num.fractional != '') { - if (options.tightSpacing) { + if (num.whole !== '' || num.fractional !== '') { + if (options["tight-spacing"]) { exponentProductNode.attributes.set('lspace', '0em'); exponentProductNode.attributes.set('rspace', '0em'); } - nodes.push(exponentProductNode); - // currentNode.appendChild(exponentProductNode); + root.appendChild(exponentProductNode); } - nodes.push(exponential); - // currentNode.appendChild(exponential); + root.appendChild(exponential); } } } } - return nodes; + return root; } export function displayNumberMml(num: INumberPiece, parser: TexParser, options: IOptions): MmlNode { - let rootNode: MmlNode; - let currentNode: MmlNode; - if (options.negativeColor != '') { - rootNode = parser.create('node', 'mstyle', [], { mathcolor: options.negativeColor }); + const rootNode: MmlNode = parser.create('node', 'mrow'); + if (num.sign === '-' && options["negative-color"]) { + rootNode.attributes.set('mathcolor', options["negative-color"]); } - let mrow = parser.create('node', 'mrow'); - currentNode = mrow; - if (rootNode === undefined) { - rootNode = mrow; - } else { - rootNode.appendChild(mrow); - } - - groupNumbersMap.get(options.groupDigits)?.(num, options); - if (options.bracketNegativeNumbers) { + groupNumbersMap.get(options["group-digits"])?.(parser, num, options); + if (options["bracket-negative-numbers"]) { if (num.sign === '-') { - const leftBracket = parser.create('token', 'mo', {}, '('); - currentNode.appendChild(leftBracket); - } - } else { - if (options.printImplicitPlus && num.sign === '') { - const sign = parser.create('token', 'mo', { rspace: "0em", lspace: "0em" }, '+'); - currentNode.appendChild(sign); - } else { - if (num.sign !== '') { - const sign = parser.create('token', 'mo', { rspace: "0em", lspace: "0em" }, num.sign); - currentNode.appendChild(sign); - } + const leftBracket = parser.create('token', 'mo', { stretchy: false }, '('); + rootNode.appendChild(leftBracket); } } + if (num.sign || options["print-implicit-plus"]) { + const sign = parser.create('token', 'mo', { rspace: "0em", lspace: "0em" }, num.sign || '+'); + rootNode.appendChild(sign); + } + let numberString = ''; let trailingMml: MmlNode; // if unity mantissa AND don't print it, then we don't need the rest of this. - if (num.whole == '1' && num.fractional == '' && !options.printUnityMantissa) { + if (num.whole === '1' && !num.fractional && !options["print-unity-mantissa"]) { // don't do anything UNLESS exponent is also zero and printZeroExponent is false - if (!options.printZeroExponent && (num.exponent == '' || (num.exponent == '1' && num.exponentSign != '-'))) { - numberString += '1'; + if (!options["print-zero-exponent"] && (!num.exponent || (num.exponent === '0' && num.exponentSign !== '-'))) { + numberString = '1'; } } else { - if ((num.whole == '' && num.fractional) || num.whole == '0') { - if (options.printZeroInteger) { - numberString += '0'; + if ((!num.whole && num.fractional) || +(num.whole) === 0) { + if (options["print-zero-integer"]) { + numberString = '0'; } } else { - numberString += num.whole; + numberString = num.whole; } - numberString += (num.decimal != '' ? options.outputDecimalMarker : ''); - if (options.zeroDecimalAsSymbol && +(num.fractional) == 0) { - trailingMml = (new TexParser(options.zeroSymbol, GlobalParser.stack.env, GlobalParser.configuration)).mml(); + numberString += (num.decimal ? options["output-decimal-marker"] : ''); + if (options["zero-decimal-as-symbol"] && +(num.fractional) === 0) { + trailingMml = (new TexParser(options["zero-symbol"], parser.stack.env, parser.configuration)).mml(); } else { numberString += num.fractional; } } const numberNode = parser.create('token', 'mn', {}, numberString); - currentNode.appendChild(numberNode); - if (trailingMml != null) { - currentNode.appendChild(trailingMml); + rootNode.appendChild(numberNode); + if (trailingMml) { + rootNode.appendChild(trailingMml); } - // display uncertanties (if not null) num.uncertainty?.forEach(v => { - const uncertaintyNode = uncertaintyModeMmlMapping.get(options.uncertaintyMode)?.(v, num, parser, options); - currentNode.appendChild(uncertaintyNode); + const uncertaintyNode = uncertaintyModeMmlMapping.get(options["uncertainty-mode"])?.(v, num, parser, options); + rootNode.appendChild(uncertaintyNode || parser.create('node', 'inferredMrow')); }); - const exponentNodes = createExponentMml( num, parser, options); - exponentNodes.forEach(v=>{ - currentNode.appendChild(v); - }); - // const exponentProductNode = (new TexParser(options.exponentProduct, GlobalParser.stack.env, GlobalParser.configuration)).mml(); - // const exponentBaseNode = (new TexParser(options.exponentBase, GlobalParser.stack.env, GlobalParser.configuration)).mml(); - // if (options.printZeroExponent && (num.exponent == '' || (num.exponent == '0'))) { - // const zeroNode = parser.create('token', 'mn', {}, '0'); - // if (options.outputExponentMarker != '') { - // const customExponentMarker = parser.create('token', 'mi', { mathvariant: 'normal' }, options.outputExponentMarker); - // currentNode.appendChild(customExponentMarker); - // currentNode.appendChild(zeroNode); - // } else { - // if (options.tightSpacing) { - // exponentProductNode.attributes.set('lspace', '0em'); - // exponentProductNode.attributes.set('rspace', '0em'); - // } - // const exponential = parser.create('node', 'msup', [exponentBaseNode, zeroNode]); - // currentNode.appendChild(exponentProductNode); - // currentNode.appendChild(exponential); - // } - // } else if (num.exponent != '' && num.exponent != '0') { - // const exponentSignNode = parser.create('token', 'mo', {}, num.exponentSign); - // const exponentValueNode = parser.create('token', 'mn', {}, num.exponent); - // const supPart = num.exponentSign === '-' - // ? parser.create('node', 'mrow', [exponentSignNode, exponentValueNode]) - // : exponentValueNode; - // const exponential = parser.create('node', 'msup', [exponentBaseNode, supPart]); - // // if unity mantissa AND don't print it, then can't print exponentMarkers (E) nor exponentProduct (\\times) - // if (num.whole == '1' && num.fractional == '' && !options.printUnityMantissa) { - // currentNode.appendChild(exponential); - // } else { - // if (num.exponentMarker != '') { - // if (options.outputExponentMarker != '') { - // const customExponentMarker = (new TexParser(options.outputExponentMarker, GlobalParser.stack.env, GlobalParser.configuration)).mml(); - // //const customExponentMarker = parser.create('token', 'mi', { }, options.outputExponentMarker); - // currentNode.appendChild(customExponentMarker); - // currentNode.appendChild(supPart); - // } else { - // if (num.whole != '' || num.fractional != '') { - // if (options.tightSpacing) { - // exponentProductNode.attributes.set('lspace', '0em'); - // exponentProductNode.attributes.set('rspace', '0em'); - // } - // currentNode.appendChild(exponentProductNode); - // } - // currentNode.appendChild(exponential); - // } - // } - // } - // } + const exponentNode = createExponentMml(num, parser, options); + rootNode.appendChild(exponentNode); - if (options.bracketNegativeNumbers) { - if (num.sign == '-') { - const rightBracket = parser.create('token', 'mo', {}, ')'); - currentNode.appendChild(rightBracket); - } - } + if (options["bracket-negative-numbers"] && num.sign === '-') { + const rightBracket = parser.create('token', 'mo', { stretchy: false }, ')'); + rootNode.appendChild(rightBracket); + } return rootNode; } -export function displayOutputMml(num: INumberPiece, parser: TexParser, options: IOptions): MmlNode[] { - let rootNodes: MmlNode[] = []; - let currentNode: MmlNode; - if (options.numberColor != '') { - currentNode = parser.create('node', 'mstyle', [], { mathcolor: options.numberColor }); - rootNodes.push(currentNode); - } else if (options.color != '') { - currentNode = parser.create('node', 'mstyle', [], { mathcolor: options.color }); - rootNodes.push(currentNode); - } - - if (num.prefix !== '') { - const prefix = (new TexParser(num.prefix, GlobalParser.stack.env, GlobalParser.configuration)).mml(); - prefix.attributes.set('rspace', '0em'); - prefix.attributes.set('lspace', '0em'); - if (currentNode == null) { - rootNodes.push(prefix); - } else { - currentNode.appendChild(prefix); - } - } - - const numberNode = displayNumberMml(num, parser, options); - if (currentNode == null) { - rootNodes.push(numberNode); - } else { - currentNode.appendChild(numberNode); +export function displayOutputMml(num: INumberPiece, parser: TexParser, options: IOptions): MmlNode { + const color = options["number-color"] || options.color; + const rootNode = parser.create('node', color ? 'mrow' : 'inferredMrow', [], color ? { mathcolor: color } : {}); + if (num.prefix) { + const prefix = (new TexParser('{' + num.prefix + '}', parser.stack.env, parser.configuration)).mml(); + rootNode.appendChild(prefix); } - - return rootNodes; + rootNode.appendChild(displayNumberMml(num, parser, options)); + return rootNode; } - -// OBSOLETE -export function displayOutput(num: INumberPiece, options: IOptions): string { - - let output = ''; - - let closeColor: boolean = false; - if (options.numberColor != '') { - output += '{\\color{' + options.numberColor + '}'; - closeColor = true; - } else if (options.color != '') { - output += '{\\color{' + options.color + '}'; - closeColor = true; - } - - // display any prefix symbol such as less than, greater than, etc. - // brackets will remove the extra space that normally occurs with symbol - output += '{' + num.prefix + '}'; - - // display main number - output += displayNumber(num, options); - - if (closeColor) { - output += '}'; - } - - return output; -} \ No newline at end of file diff --git a/src/numMethods.ts b/src/numMethods.ts index 58a7da8..56820ca 100644 --- a/src/numMethods.ts +++ b/src/numMethods.ts @@ -5,6 +5,7 @@ import { displayOutputMml } from "./numDisplayMethods"; import { postProcessNumber } from "./numPostProcessMethods"; import { findOptions, IOptions } from "./options/options"; import { INumOptions, INumParseOptions } from "./options/numberOptions"; +import { siunitxError } from "./error/errors"; @@ -52,17 +53,17 @@ export function generateNumberPiece(): INumberPiece { return piece; } -export function pieceToNumber(piece:INumberPiece): number { +export function pieceToNumber(piece: INumberPiece): number { let build = piece.sign + piece.whole; - if (piece.fractional !== ''){ + if (piece.fractional !== '') { build += '.' + piece.fractional; - } - if (piece.exponent !== ''){ - build += 'e'+piece.exponentSign + piece.exponent; + } + if (piece.exponent !== '') { + build += 'e' + piece.exponentSign + piece.exponent; } try { let result = Number.parseFloat(build); - if (Number.isNaN(result)){ + if (Number.isNaN(result)) { result = 0; } return result; @@ -71,16 +72,20 @@ export function pieceToNumber(piece:INumberPiece): number { } } -function parseDigits(text: string, numPiece: INumberPiece) { - let num: INumberPiece; +// INumberPiece is built from left to right, so we're always working on the latest part... which could be uncertainty. So get the last piece. +function getLastNumPiece(numPiece: INumberPiece):INumberPiece{ if (numPiece.uncertainty.length > 0) { - num = numPiece.uncertainty[numPiece.uncertainty.length - 1]; + return numPiece.uncertainty[numPiece.uncertainty.length - 1]; } else { - num = numPiece; + return numPiece; } - if (num.exponentMarker != '') { +} + +function parseDigits(text: string, numPiece: INumberPiece) { + const num = getLastNumPiece(numPiece); + if (num.exponentMarker !== '') { num.exponent += text; - } else if (num.decimal != '') { + } else if (num.decimal !== '') { num.fractional += text; } else { num.whole += text; @@ -88,23 +93,16 @@ function parseDigits(text: string, numPiece: INumberPiece) { } function parseDecimals(text: string, numPiece: INumberPiece) { - let num: INumberPiece; - if (numPiece.uncertainty.length > 0) { - num = numPiece.uncertainty[numPiece.uncertainty.length - 1]; - } else { - num = numPiece; - } + const num = getLastNumPiece(numPiece); num.decimal += text; } function parseComparators(text: string, numPiece: INumberPiece) { - let num: INumberPiece; - if (numPiece.uncertainty.length > 0) { - num = numPiece.uncertainty[numPiece.uncertainty.length - 1]; - } else { - num = numPiece; - } + const num = getLastNumPiece(numPiece); + if (num.prefix !== ''){ + throw siunitxError.ComparatorAlreadySet(num.prefix, text); + } num.prefix += text; } @@ -120,13 +118,8 @@ function parseExponentMarkers(text: string, numPiece: INumberPiece) { } function parseSigns(text: string, numPiece: INumberPiece) { - let num: INumberPiece; - if (numPiece.uncertainty.length > 0) { - num = numPiece.uncertainty[numPiece.uncertainty.length - 1]; - } else { - num = numPiece; - } - if (num.exponentMarker != '') { + const num = getLastNumPiece(numPiece); + if (num.exponentMarker !== '') { num.exponentSign += text; } else { num.sign += text; @@ -139,8 +132,8 @@ function parseOpenUncertainty(text: string, numPiece: INumberPiece) { } function parseCloseUncertainty(text: string, numPiece: INumberPiece) { - if (numPiece.uncertainty.length == 0) { - throw new TexError('50', 'No uncertainty parsed to close.'); + if (numPiece.uncertainty.length === 0) { + throw new TexError('50', 'Trying to close an uncertainty that doesn\'t exist.'); } const uncertainty = numPiece.uncertainty[numPiece.uncertainty.length - 1]; if (uncertainty.completed) { @@ -159,59 +152,47 @@ function parseIgnore() { } // using two types for output. Ex. \\pm is used both as sign and as an uncertainty. Need map of map for this one. -export function generateNumberMapping(options: INumParseOptions): Map> { - const parseMap = new Map>(); - //parseMap.set('\\', parseMacro); - let tempArray; - const matchMacrosOrChar = /[^\\\s]|(?:\\[^\\]*(?=\s|\\|$))/g; - while ((tempArray = matchMacrosOrChar.exec(options.inputComparators)) !== null) { - parseMap.set(tempArray[0], parseComparators); - } - while ((tempArray = matchMacrosOrChar.exec(options.inputSigns)) !== null) { - parseMap.set(tempArray[0], parseSigns); - } - while ((tempArray = matchMacrosOrChar.exec(options.inputDigits)) !== null) { - parseMap.set(tempArray[0], parseDigits); - } - while ((tempArray = matchMacrosOrChar.exec(options.inputDecimalMarkers)) !== null) { - parseMap.set(tempArray[0], parseDecimals); - } - while ((tempArray = matchMacrosOrChar.exec(options.inputOpenUncertainty)) !== null) { - parseMap.set(tempArray[0], parseOpenUncertainty); - } - while ((tempArray = matchMacrosOrChar.exec(options.inputCloseUncertainty)) !== null) { - parseMap.set(tempArray[0], parseCloseUncertainty); - } - while ((tempArray = matchMacrosOrChar.exec(options.inputUncertaintySigns)) !== null) { - if (parseMap.has(tempArray[0])) { - const firstFunc = parseMap.get(tempArray[0]) as CharNumFunction; - const innerMap = new Map(); - innerMap.set('inputSigns', firstFunc); - innerMap.set('inputUncertaintySigns', parseUncertaintySigns); - parseMap.set(tempArray[0], innerMap); - } else { - parseMap.set(tempArray[0], parseUncertaintySigns); +export function generateNumberMapping(options: INumParseOptions): Map { + const parseMap = new Map(); + const matchMacrosOrChar = /\\(?:[a-zA-Z]+|[\uD800-\uDBFF].|.)|[\uD800-\uDBFF].|[^\s\\]/g; + for (const [key, method] of [ + ['input-comparators', parseComparators], + ['input-signs', parseSigns], + ['input-digits', parseDigits], + ['input-decimal-markers', parseDecimals], + ['input-open-uncertainty', parseOpenUncertainty], + ['input-close-uncertainty', parseCloseUncertainty], + ['input-uncertainty-signs', parseUncertaintySigns], + ['input-exponent-markers', parseExponentMarkers], + ['input-ignore', parseIgnore] + ] as [string, CharNumFunction][]) { + const option = options[key]; + if (option.match(/(?:^|[^\\])(?:\\\\)*\\$/)) { + throw siunitxError.BadOptionChars(key); } + (option.match(matchMacrosOrChar) || []).forEach((c: string) => { + if (parseMap.has(c) && key === 'input-uncertainty-signs') { + const inputSigns = parseMap.get(c) as CharNumFunction; + const altMethod: CharNumFunction = function (macro, num) { + (num.whole === '' && num.decimal === '' ? inputSigns : parseUncertaintySigns)(macro, num); + } + parseMap.set(c, altMethod); + } else { + parseMap.set(c, method); + } + }); } - while ((tempArray = matchMacrosOrChar.exec(options.inputExponentMarkers)) !== null) { - parseMap.set(tempArray[0], parseExponentMarkers); - } - while ((tempArray = matchMacrosOrChar.exec(options.inputIgnore)) !== null) { - parseMap.set(tempArray[0], parseIgnore); - } - return parseMap; } export function parseNumber(parser: TexParser, text: string, options: INumOptions): INumberPiece { - const mapping = generateNumberMapping(options); - text = text.replace('<<', '\\ll'); - text = text.replace('>>', '\\gg'); - text = text.replace('<=', '\\le'); - text = text.replace('>=', '\\ge'); - text = text.replace('+-', '\\pm'); + text = text.replace('<<', '\\ll') + .replace('>>', '\\gg') + .replace('<=', '\\le') + .replace('>=', '\\ge') + .replace('+-', '\\pm'); const num: INumberPiece = generateNumberPiece(); @@ -219,65 +200,40 @@ export function parseNumber(parser: TexParser, text: string, options: INumOption subParser.i = 0; // process character // if '\', then read until next '\' or whitespace char + + let token; while (subParser.i < subParser.string.length) { - let char = subParser.string.charAt(subParser.i); - subParser.i++; - if (char != '\\') { - if (mapping.has(char)) { - const func = mapping.get(char); - if (typeof func == 'function') { - (mapping.get(char) as CharNumFunction)(char, num); - } else { - if (num.whole == '' && num.decimal == '') { - (func as Map).get('inputSigns')?.(char, num); - } else { - (func as Map).get('inputUncertaintySigns')?.(char, num); - } - } - } - } else { - let macro = char; - char = ''; - while (subParser.i < subParser.string.length && char != '\\' && char != ' ') { - char = subParser.string.charAt(subParser.i); - if (char != '\\' && char != ' ') { - macro += char; - } - subParser.i++; - } + token = subParser.GetNext(); + subParser.i++; // GetNext() does not advance position unless skipping whitespace - if (mapping.has(macro)) { - const func = mapping.get(macro); - if (typeof func == 'function') { - (mapping.get(macro) as CharNumFunction)(macro, num); - } else { - if (num.whole == '' && num.decimal == '') { - (func as Map).get('inputSigns')?.(macro, num); - } else { - (func as Map).get('inputUncertaintySigns')?.(macro, num); - } - } - } + if (token === '\\') { + token += subParser.GetCS(); + } + + try { + mapping.get(token)(token, num); + } catch { + throw siunitxError.InvalidNumArgument(subParser.string); } } - if (!options.retainExplicitDecimalMarker && num.decimal != '' && num.fractional == '') { + if (!options["retain-explicit-decimal-marker"] && num.decimal !== '' && num.fractional === '') { num.decimal = ''; } - if (!options.retainExplicitPlus && num.sign == '+') { + if (!options["retain-explicit-plus"] && num.sign === '+') { num.sign = ''; } // adding exponent to value check here. Without it, exponentials without a base won't stay negative. (-e10) - const value = +(num.whole + (num.decimal != '' ? '.' : '') + num.fractional + (num.exponent == '' ? '' : 'e' + num.exponentSign + num.exponent)); - if (value == 0 && !options.retainNegativeZero && num.sign == '-') { + const value = +(num.whole + (num.decimal !== '' ? '.' : '') + num.fractional + (num.exponent === '' ? '' : 'e' + num.exponentSign + num.exponent)); + if (value === 0 && !options["retain-negative-zero"] && num.sign === '-') { num.sign = ''; } - if (!options.retainZeroUncertainty) { + if (!options["retain-zero-uncertainty"]) { for (let i = num.uncertainty.length - 1; i >= 0; i--) { - const uncertaintyValue = +(num.uncertainty[i].whole + (num.uncertainty[i].decimal != '' ? '.' : '') + num.uncertainty[i].fractional); - if (uncertaintyValue == 0) { + const uncertaintyValue = +(num.uncertainty[i].whole + (num.uncertainty[i].decimal !== '' ? '.' : '') + num.uncertainty[i].fractional); + if (uncertaintyValue === 0) { num.uncertainty.splice(i, 1); } } @@ -286,10 +242,10 @@ export function parseNumber(parser: TexParser, text: string, options: INumOption return num; } -export function processNumber(parser: TexParser): MmlNode[] { - const globalOptions: IOptions = { ...parser.options as IOptions }; +export function processNumber(parser: TexParser): MmlNode { + const globalOptions: IOptions = { ...parser.options.siunitx as IOptions }; - const localOptions = findOptions(parser); + const localOptions = findOptions(parser, globalOptions); //processOptions(globalOptions, localOptionString); //const options = processOptions(globalOptions, localOptionString); @@ -298,31 +254,28 @@ export function processNumber(parser: TexParser): MmlNode[] { let text = parser.GetArgument('num'); - if (globalOptions.parseNumbers) { + if (globalOptions["parse-numbers"]) { // going to assume evaluate expression is processed first, THEN the result is parsed normally - if (globalOptions.evaluateExpression) { + if (globalOptions["evaluate-expression"]) { // TODO Sanitize Evaluate Expression! let expression = globalOptions.expression expression = expression.replace('#1', text); - let result = eval(expression); - text = result.toString(); + text = eval(expression).toString(); } - const num = parseNumber(parser, text, globalOptions); - postProcessNumber(num, globalOptions); - + postProcessNumber(parser, num, globalOptions); //const displayResult = displayOutput(num, globalOptions); - const mmlNodes = displayOutputMml(num, parser, globalOptions); + const mmlNode = displayOutputMml(num, parser, globalOptions); //const mml = (new TexParser(displayResult, parser.stack.env, parser.configuration)).mml(); - return mmlNodes; + return mmlNode; } else { const mml = (new TexParser(text, parser.stack.env, parser.configuration)).mml(); - return [mml]; + return mml; } } diff --git a/src/numPostProcessMethods.ts b/src/numPostProcessMethods.ts index 24f15c0..8fd7e45 100644 --- a/src/numPostProcessMethods.ts +++ b/src/numPostProcessMethods.ts @@ -1,123 +1,123 @@ -import { siunitxError } from "./errors"; +import TexParser from "mathjax-full/js/input/tex/TexParser"; +import { siunitxError } from "./error/errors"; import { INumberPiece, parseNumber } from "./numMethods"; import { INumOptions, INumPostOptions } from "./options/numberOptions"; -import { IOptions } from "./options/options"; +import { IOptions } from "./options/options"; -import { GlobalParser } from "./siunitx"; -function convertToScientific(numOriginal:INumberPiece, options: INumPostOptions) : INumberPiece { +function convertToScientific(parser: TexParser, numOriginal: INumberPiece, options: INumPostOptions): INumberPiece { //convert to actual number and use formating to print scientific - let num = JSON.parse(JSON.stringify(numOriginal)); - const val = (+(num.sign + num.whole + num.decimal + num.fractional + (num.exponent != '' ? ('e' + num.exponentSign + num.exponent): '' ))).toExponential(); + const num = JSON.parse(JSON.stringify(numOriginal)); + const val = (+(num.sign + num.whole + num.decimal + num.fractional + (num.exponent !== '' ? ('e' + num.exponentSign + num.exponent) : ''))).toExponential(); // parse that back in - const newNum = parseNumber(GlobalParser, val, options as IOptions); + const newNum = parseNumber(parser, val, options as IOptions); //don't forget to check for trailing zeros and put them back let trailingZeros = 0; // count trailing zeros in original fractional part - if (num.fractional != ''){ - for (let i=num.fractional.length-1; i>=0; i--){ - if (num.fractional[i] == '0'){ + if (num.fractional !== '') { + for (let i = num.fractional.length - 1; i >= 0; i--) { + if (num.fractional[i] === '0') { trailingZeros++; - } else{ + } else { break; } } } // count trailing zeros in original whole part (if all of fractional part was zeros) - if (num.whole != '' && num.fractional.length == trailingZeros){ - for (let i=num.whole.length-1; i>=0; i--){ - if (num.whole[i] == '0'){ + if (num.whole !== '' && num.fractional.length === trailingZeros) { + for (let i = num.whole.length - 1; i >= 0; i--) { + if (num.whole[i] === '0') { trailingZeros++; - } else{ + } else { break; } } } // add the appropriate number of trailing zeros. - for(let i=0; i 0){ + if (newNum.decimal === '' && trailingZeros > 0) { newNum.decimal = '.'; } // copy the new values to the original reference - for (const prop in num){ + for (const prop in num) { num[prop] = newNum[prop]; } return num; } -function convertToXExponent(num:INumberPiece, targetExponent: number){ - if (num == null) return; +function convertToExponent(num: INumberPiece, targetExponent: number) { + if (!num) return; // count difference between target exponent and current one. const diff = targetExponent - +(num.exponentSign + num.exponent); const dir = Math.sign(diff); // -: move numbers from frac to whole, +: move the other way - for (let i=0; i< Math.abs(diff); i++){ - if (dir < 0){ - if (num.fractional.length > 0){ - num.whole = num.whole + num.fractional.slice(0,1); + for (let i = 0; i < Math.abs(diff); i++) { + if (dir < 0) { + if (num.fractional.length > 0) { + num.whole = num.whole + num.fractional.slice(0, 1); num.fractional = num.fractional.slice(1, num.fractional.length); } else { num.whole = num.whole + '0'; } } else { - if (num.whole.length > 0){ - num.fractional = num.whole.slice(num.whole.length-1, num.whole.length) + num.fractional; + if (num.whole.length > 0) { + num.fractional = num.whole.slice(num.whole.length - 1, num.whole.length) + num.fractional; num.whole = num.whole.slice(0, num.whole.length - 1); } else { num.fractional = '0' + num.fractional; } } } - if (num.fractional != '' && num.decimal == ''){ + if (num.fractional !== '' && num.decimal === '') { num.decimal = '.'; } num.exponent = Math.abs(targetExponent).toString(); num.exponentSign = Math.sign(targetExponent) < 0 ? '-' : ''; } -function convertToEngineering(num:INumberPiece, options: INumPostOptions):void { +function convertToEngineering(parser: TexParser, num: INumberPiece, options: INumPostOptions): void { // similar to convertToFixed except we calculate the exponent to be a power of three that keeps the whole number part non-zero. - + // convert to scientific, then move decimal... - const convertedNum = convertToScientific(num, options); + const convertedNum = convertToScientific(parser, num, options); Object.assign(num, convertedNum); let targetExponent = +(num.exponentSign + num.exponent); - while (targetExponent % 3 != 0) { + while (targetExponent % 3 !== 0) { targetExponent--; } - - convertToXExponent(num, targetExponent); + + convertToExponent(num, targetExponent); } -export function convertToFixed(num:INumberPiece, options: INumPostOptions):void { +export function convertToFixed(parser: TexParser, num: INumberPiece, options: INumPostOptions): void { // convert to scientific, then move decimal... - const convertedNum = convertToScientific(num, options); + const convertedNum = convertToScientific(parser, num, options); Object.assign(num, convertedNum); - - convertToXExponent(num, options.fixedExponent); + + convertToExponent(num, options["fixed-exponent"]); } -const exponentModeMap = new Mapvoid>([ +const exponentModeMap = new Map void>([ // eslint-disable-next-line @typescript-eslint/no-empty-function - ['input', ():void => { }], // leave number as-is + ['input', (): void => { }], // leave number as-is ['fixed', convertToFixed], ['engineering', convertToEngineering], - ['scientific', (num: INumberPiece, options: IOptions) => { - const convertedNum = convertToScientific(num,options); + ['scientific', (parser: TexParser, num: INumberPiece, options: IOptions) => { + const convertedNum = convertToScientific(parser, num, options); Object.assign(num, convertedNum); }], - ['threshold', (num: INumberPiece, options: IOptions)=>{ - const minMax = options.exponentThresholds.split(':'); - if (minMax.length != 2){ - throw siunitxError.ExponentThresholdsError(options.exponentThresholds); + ['threshold', (parser: TexParser, num: INumberPiece, options: IOptions) => { + const minMax = options["exponent-thresholds"].split(':'); + if (minMax.length !== 2) { + throw siunitxError.ExponentThresholdsError(options["exponent-thresholds"]); } // ensure we have a version in scientific form but leave the original alone. - const testNum = convertToScientific(num,options); + const testNum = convertToScientific(parser, num, options); const testExponent = +(testNum.exponentSign + testNum.exponent); - if ( testExponent > +minMax[0] && testExponent < +minMax[1]){ + if (testExponent > +minMax[0] && testExponent < +minMax[1]) { //leave number as-is' } else { // copy the scientific form over to the original INumPiece @@ -126,13 +126,13 @@ const exponentModeMap = new Map 5){ - result = true; - } else if (firstDrop == 5) { - if (roundEven){ - if (toRound % 2 == 0){ + if (firstDrop > 5) { + result = true; + } else if (firstDrop === 5) { + if (roundEven) { + if (toRound % 2 === 0) { result = false; } else { result = true; @@ -145,16 +145,16 @@ function shouldRoundUp(toRound:number, firstDrop:number, roundEven:boolean):bool return result; } -function roundUp(fullNumber:string, position:number):string{ +function roundUp(fullNumber: string, position: number): string { let result = ''; const reverseNumArr = new Array(); let digit = +fullNumber[position] + 1; - let roundedNine = digit == 0 ? true : false; - reverseNumArr.push(digit); - for (let i=position-1; i >= 0; i--) { - if (roundedNine){ + let roundedNine = digit === 0 ? true : false; + reverseNumArr.push(digit); + for (let i = position - 1; i >= 0; i--) { + if (roundedNine) { digit = +fullNumber[i] + 1; - roundedNine = digit == 0 ? true : false; + roundedNine = digit === 0 ? true : false; reverseNumArr.push(digit); } else { digit = +fullNumber[i]; @@ -162,66 +162,66 @@ function roundUp(fullNumber:string, position:number):string{ } } reverseNumArr.reverse(); - reverseNumArr.forEach(v=> result+=v); + reverseNumArr.forEach(v => result += v); return result; } -function roundPlaces(num:INumberPiece, options: INumPostOptions):void{ +function roundPlaces(parser: TexParser, num: INumberPiece, options: INumPostOptions): void { // if uncertainty exists, no rounding at all! - if (num.uncertainty.length == 0) { - if (num.fractional.length > options.roundPrecision ) { - const firstDrop = +num.fractional.slice(options.roundPrecision, options.roundPrecision+1); - const toRound = +num.fractional.slice(options.roundPrecision - 1, options.roundPrecision); + if (num.uncertainty.length === 0) { + if (num.fractional.length > options["round-precision"]) { + const firstDrop = +num.fractional.slice(options["round-precision"], options["round-precision"] + 1); + const toRound = +num.fractional.slice(options["round-precision"] - 1, options["round-precision"]); const wholeLength = num.whole === '0' ? 0 : num.whole.length; - if (shouldRoundUp(toRound, firstDrop, options.roundHalf == 'even')){ - const result = roundUp((num.whole === '0' ? '' : num.whole) + num.fractional, wholeLength + options.roundPrecision - 1); + if (shouldRoundUp(toRound, firstDrop, options["round-half"] === 'even')) { + const result = roundUp((num.whole === '0' ? '' : num.whole) + num.fractional, wholeLength + options["round-precision"] - 1); //const wholeLength = num.whole.length; - num.whole = result.slice(0,wholeLength); + num.whole = result.slice(0, wholeLength); num.fractional = result.slice(wholeLength, result.length); } else { - num.fractional = num.fractional.slice(0, options.roundPrecision); - } + num.fractional = num.fractional.slice(0, options["round-precision"]); + } - } else if (num.fractional.length < options.roundPrecision && options.roundPad) { - const toAdd = options.roundPrecision-num.fractional.length; - for (let i = 0; i < toAdd; i++){ + } else if (num.fractional.length < options["round-precision"] && options["round-pad"]) { + const toAdd = options["round-precision"] - num.fractional.length; + for (let i = 0; i < toAdd; i++) { num.fractional += '0'; // pad with zeros } - if (num.decimal === ''){ - num.decimal = (options as INumOptions).outputDecimalMarker; + if (!num.decimal) { + num.decimal = (options as INumOptions)["output-decimal-marker"]; } } else { //no rounding needed. } - afterRoundZeroOptions(num, options); + afterRoundZeroOptions(parser, num, options); } } -function roundFigures(num:INumberPiece, options: INumPostOptions):void{ +function roundFigures(parser: TexParser, num: INumberPiece, options: INumPostOptions): void { // if uncertainty exists, no rounding at all! - if (num.uncertainty.length == 0) { + if (num.uncertainty.length === 0) { // whole can't be '0', and converting fractional to number and back to string gets rid of leading zeros. - const combined = num.whole === '0' ? (+num.fractional).toString() : num.whole + (+num.fractional).toString(); - if (combined.length > options.roundPrecision ) { + const combined = num.whole === '0' ? (+num.fractional).toString() : num.whole + (+num.fractional).toString(); + if (combined.length > options["round-precision"]) { //console.log(num.whole + num.decimal + num.fractional); - const firstDrop = +combined.slice(options.roundPrecision, options.roundPrecision+1); - const toRound = +combined.slice(options.roundPrecision - 1, options.roundPrecision); - - let roundingResult:string; + const firstDrop = +combined.slice(options["round-precision"], options["round-precision"] + 1); + const toRound = +combined.slice(options["round-precision"] - 1, options["round-precision"]); + + let roundingResult: string; // round up or down - if (shouldRoundUp(toRound, firstDrop, options.roundHalf == 'even')){ - roundingResult = roundUp(combined, options.roundPrecision - 1); + if (shouldRoundUp(toRound, firstDrop, options["round-half"] === 'even')) { + roundingResult = roundUp(combined, options["round-precision"] - 1); } else { - roundingResult = combined.slice(0, options.roundPrecision); + roundingResult = combined.slice(0, options["round-precision"]); } // split the result back into whole and fractional parts const wholeLength = num.whole === '0' ? 0 : num.whole.length; - if (roundingResult.length >= wholeLength){ + if (roundingResult.length >= wholeLength) { // need to add leading zeroes to fractional part maybe // if whole was zero, check if original fractional had leading zeroes - if (wholeLength == 0){ + if (wholeLength === 0) { num.fractional = ''.padEnd(num.fractional.length - (+num.fractional).toString().length, '0'); } else { num.fractional = ''; @@ -232,41 +232,41 @@ function roundFigures(num:INumberPiece, options: INumPostOptions):void{ num.decimal = ''; const addZeros = wholeLength - roundingResult.length; num.whole = roundingResult; - for (let i = 0; i 0){ + if (num.uncertainty.length > 0) { // just in case convert uncertainty to bracket form... easier to round - num.uncertainty.forEach(uncertainty=>{ - if (uncertainty.type == 'pm') { + num.uncertainty.forEach(uncertainty => { + if (uncertainty.type === 'pm') { //easiest way is to convert to a number and check if less than zero const strNum = uncertainty.whole + uncertainty.decimal + uncertainty.fractional; const num = +(strNum); // if less than 1 (just a fraction), then remove leading zeros. Else leave it as is. if (num < 1) { - let position=0; - for (let i=0; i{ + num.uncertainty.forEach(uncertainty => { smallest = Math.min(uncertainty.whole.length, smallest); - - if (uncertainty.whole.length - options.roundPrecision > 0){ - const firstDrop = +uncertainty.whole.slice(options.roundPrecision, options.roundPrecision+1); - const toRound = +uncertainty.whole.slice(options.roundPrecision - 1, options.roundPrecision); - - if (shouldRoundUp(toRound, firstDrop, options.roundHalf == 'even')){ - uncertainty.whole = roundUp(uncertainty.whole, options.roundPrecision - 1); + + if (uncertainty.whole.length - options["round-precision"] > 0) { + const firstDrop = +uncertainty.whole.slice(options["round-precision"], options["round-precision"] + 1); + const toRound = +uncertainty.whole.slice(options["round-precision"] - 1, options["round-precision"]); + + if (shouldRoundUp(toRound, firstDrop, options["round-half"] === 'even')) { + uncertainty.whole = roundUp(uncertainty.whole, options["round-precision"] - 1); } else { - uncertainty.whole = uncertainty.whole.slice(0, options.roundPrecision); - } + uncertainty.whole = uncertainty.whole.slice(0, options["round-precision"]); + } } }); - - const mainRemove = smallest - options.roundPrecision; - if (mainRemove > 0){ + const mainRemove = smallest - options["round-precision"]; + + if (mainRemove > 0) { const combined = num.whole + num.fractional; const precision = combined.length - mainRemove; - const firstDrop = +combined.slice(precision, precision+1); + const firstDrop = +combined.slice(precision, precision + 1); const toRound = +combined.slice(precision - 1, precision); - + let roundingResult; // round up or down - if (shouldRoundUp(toRound, firstDrop, options.roundHalf == 'even')){ + if (shouldRoundUp(toRound, firstDrop, options["round-half"] === 'even')) { roundingResult = roundUp(combined, precision - 1); } else { roundingResult = combined.slice(0, precision); } // split the result back into whole and fractional parts - if (roundingResult.length >= num.whole.length){ + if (roundingResult.length >= num.whole.length) { num.fractional = roundingResult.slice(num.whole.length, roundingResult.length); } else { num.fractional = ''; num.decimal = ''; const addZeros = num.whole.length - roundingResult.length; num.whole = roundingResult; - for (let i = 0; ioptions); + const current = Math.abs(+(num.whole + num.decimal + num.fractional + (num.exponentMarker !== '' ? 'e' : '') + num.exponentSign + num.exponent)); + if (current === 0) { + if (options["round-minimum"] !== '0') { + num.prefix = '\\lt'; + const minimumNum = parseNumber(parser, options["round-minimum"], options); num.sign = minimumNum.sign; num.whole = minimumNum.whole; num.decimal = minimumNum.decimal; @@ -344,72 +344,72 @@ function afterRoundZeroOptions(num:INumberPiece, options: INumPostOptions){ num.exponentMarker = minimumNum.exponentMarker; num.exponentSign = minimumNum.exponentSign; num.exponent = minimumNum.exponent; - - } else if (options.roundZeroPositive){ + + } else if (options["round-zero-positive"]) { num.sign = ''; } } } -const roundModeMap = new Mapvoid>([ +const roundModeMap = new Map void>([ // eslint-disable-next-line @typescript-eslint/no-empty-function - ['none', ():void =>{ }], + ['none', (): void => { }], ['places', roundPlaces], ['figures', roundFigures], ['uncertainty', roundUncertainty] ]); -export function postProcessNumber(num:INumberPiece, options: INumPostOptions){ - +export function postProcessNumber(parser: TexParser, num: INumberPiece, options: INumPostOptions) { + // Post-process special case for uncertainty: 123 +- 4.5 // This number is actually 123.0 +- 4.5 or 123.0(4.5) or 123.0(45) // Even if uncertainty is dropped, the original value had a significant zero in the tenths place. // Check fraction length of uncertainty[0] and compare to value fraction length. // Could theoretically check for equal uncertainty precision in the case of two uncertainties... - if (num.uncertainty && num.uncertainty.length > 0 && num.uncertainty[0].fractional.length > num.fractional.length){ - if (num.decimal === ''){ + if (num.uncertainty && num.uncertainty.length > 0 && num.uncertainty[0].fractional.length > num.fractional.length) { + if (!num.decimal) { num.decimal = '.'; } num.fractional = num.fractional.padEnd(num.uncertainty[0].fractional.length, '0'); - } + } - if (options.dropUncertainty){ + if (options["drop-uncertainty"]) { num.uncertainty.splice(0, num.uncertainty.length); } - if (options.dropExponent){ + if (options["drop-exponent"]) { num.exponentMarker = ''; num.exponentSign = ''; num.exponent = ''; } - roundModeMap.get(options.roundMode)(num, options); + roundModeMap.get(options["round-mode"])(parser, num, options); - if (options.dropZeroDecimal && +(num.fractional) == 0){ + if (options["drop-zero-decimal"] && +(num.fractional) === 0) { num.fractional = ''; num.decimal = ''; } - if (options.minimumIntegerDigits > 0){ - const pad = options.minimumIntegerDigits - num.whole.length; - if (pad > 0){ - for (let i=0; i 0) { + const pad = options["minimum-integer-digits"] - num.whole.length; + if (pad > 0) { + for (let i = 0; i < pad; i++) { num.whole = '0' + num.whole; } } } - if (options.minimumDecimalDigits > 0){ - const pad = options.minimumDecimalDigits - num.fractional.length; - if (pad > 0){ - for (let i=0; i 0) { + const pad = options["minimum-decimal-digits"] - num.fractional.length; + if (pad > 0) { + for (let i = 0; i < pad; i++) { num.fractional += '0'; } } } - - exponentModeMap.get(options.exponentMode)(num, options); + + exponentModeMap.get(options["exponent-mode"])(parser, num, options); // remove any explicit plus in exponent - if (num.exponentSign == '+') + if (num.exponentSign === '+') num.exponentSign = ''; } diff --git a/src/numlistMethods.ts b/src/numlistMethods.ts index 32cef37..b187895 100644 --- a/src/numlistMethods.ts +++ b/src/numlistMethods.ts @@ -9,29 +9,29 @@ import { ExponentsMode } from "./options/listOptions"; export interface IExponentModeOutput { leading?: MmlNode; numbers: INumberPiece[]; - trailing?: MmlNode[]; + trailing?: MmlNode; } export const bracketOpenMap = new Map string>([ - ['\\numlist', (options: IOptions) => options.listOpenBracket], - ['\\numproduct', (options: IOptions) => options.productOpenBracket], - ['\\numrange', (options: IOptions) => options.rangeOpenBracket], - ['\\qtylist', (options: IOptions) => options.listOpenBracket], - ['\\qtyproduct', (options: IOptions) => options.productOpenBracket], - ['\\qtyrange', (options: IOptions) => options.rangeOpenBracket], + ['\\numlist', (options: IOptions) => options["list-open-bracket"]], + ['\\numproduct', (options: IOptions) => options["product-open-bracket"]], + ['\\numrange', (options: IOptions) => options["range-open-bracket"]], + ['\\qtylist', (options: IOptions) => options["list-open-bracket"]], + ['\\qtyproduct', (options: IOptions) => options["product-open-bracket"]], + ['\\qtyrange', (options: IOptions) => options["range-open-bracket"]], ]); export const bracketCloseMap = new Map string>([ - ['\\numlist', (options: IOptions) => options.listCloseBracket], - ['\\numproduct', (options: IOptions) => options.productCloseBracket], - ['\\numrange', (options: IOptions) => options.rangeCloseBracket], - ['\\qtylist', (options: IOptions) => options.listCloseBracket], - ['\\qtyproduct', (options: IOptions) => options.productCloseBracket], - ['\\qtyrange', (options: IOptions) => options.rangeCloseBracket], + ['\\numlist', (options: IOptions) => options["list-close-bracket"]], + ['\\numproduct', (options: IOptions) => options["product-close-bracket"]], + ['\\numrange', (options: IOptions) => options["range-close-bracket"]], + ['\\qtylist', (options: IOptions) => options["list-close-bracket"]], + ['\\qtyproduct', (options: IOptions) => options["product-close-bracket"]], + ['\\qtyrange', (options: IOptions) => options["range-close-bracket"]], ]); export const exponentListModeMap = new Map IExponentModeOutput>([ - ['individual', (nums: INumberPiece[], parser: TexParser, options: IOptions) => { + ['individual', (nums: INumberPiece[], _parser: TexParser, _options: IOptions) => { // do nothing return { numbers: nums }; }], @@ -45,7 +45,7 @@ export const exponentListModeMap = new Map { - const exponentNodes = createExponentMml(nums[0], parser, options); + const exponentNode = createExponentMml(nums[0], parser, options); nums.forEach(x => { x.exponent = ''; x.exponentMarker = ''; @@ -54,53 +54,57 @@ export const exponentListModeMap = new Map MmlNode[]>([ +const listNumberMap = new Map MmlNode>([ [1, (nums: INumberPiece[], parser: TexParser, options: IOptions) => { return displayOutputMml(nums[0], parser, options); }], [2, (nums: INumberPiece[], parser: TexParser, options: IOptions) => { - const exponentMapItem = exponentListModeMap.get(options.listExponents); + const exponentMapItem = exponentListModeMap.get(options["list-exponents"]); const exponentResult = exponentMapItem(nums, parser, options); const first = displayOutputMml(exponentResult.numbers[0], parser, options); - const separator = (new TexParser(`\\text{${options.listPairSeparator}}`, parser.stack.env, parser.configuration)).mml(); + const separator = (new TexParser(`\\text{${options["list-pair-separator"]}}`, parser.stack.env, parser.configuration)).mml(); const second = displayOutputMml(exponentResult.numbers[1], parser, options); - let total = []; + const root = parser.create('node', 'inferredMrow', [], {}); if (exponentResult.leading) { - total.push(exponentResult.leading); + root.appendChild(exponentResult.leading); } - total = total.concat(first).concat(separator).concat(second); + root.appendChild(first); + root.appendChild(separator); + root.appendChild(second); if (exponentResult.trailing) { - total = total.concat(exponentResult.trailing); + root.appendChild(exponentResult.trailing); } - return total; + return root; }], [3, (nums: INumberPiece[], parser: TexParser, options: IOptions) => { - const exponentMapItem = exponentListModeMap.get(options.listExponents); + const exponentMapItem = exponentListModeMap.get(options["list-exponents"]); const exponentResult = exponentMapItem(nums, parser, options); - let total = []; + const root = parser.create('node', 'inferredMrow', [], {}); if (exponentResult.leading) { - total.push(exponentResult.leading); + root.appendChild(exponentResult.leading); } - total = total.concat(displayOutputMml(exponentResult.numbers[0], parser, options)); + root.appendChild(displayOutputMml(exponentResult.numbers[0], parser, options)); for (let i = 1; i < nums.length - 1; i++) { - const separator = (new TexParser(`\\text{${options.listSeparator}}`, parser.stack.env, parser.configuration)).mml(); + const separator = (new TexParser(`\\text{${options["list-separator"]}}`, parser.stack.env, parser.configuration)).mml(); const next = displayOutputMml(exponentResult.numbers[i], parser, options); - total = total.concat(separator).concat(next); + root.appendChild(separator); + root.appendChild(next); } - const finalSeparator = (new TexParser(`\\text{${options.listFinalSeparator}}`, parser.stack.env, parser.configuration)).mml(); + const finalSeparator = (new TexParser(`\\text{${options["list-final-separator"]}}`, parser.stack.env, parser.configuration)).mml(); const last = displayOutputMml(exponentResult.numbers[exponentResult.numbers.length - 1], parser, options); - total = total.concat(finalSeparator).concat(last); + root.appendChild(finalSeparator); + root.appendChild(last); if (exponentResult.trailing) { - total = total.concat(exponentResult.trailing); + root.appendChild(exponentResult.trailing); } - return total; + return root; }] ]); @@ -113,47 +117,45 @@ export function parseList(parser: TexParser, input: string, options: IOptions): } export function processNumberList(parser: TexParser): void { - const globalOptions: IOptions = { ...parser.options as IOptions }; + const globalOptions: IOptions = { ...parser.options.siunitx as IOptions }; - const localOptions = findOptions(parser); + const localOptions = findOptions(parser, globalOptions); Object.assign(globalOptions, localOptions); let text = parser.GetArgument('num'); - if (globalOptions.parseNumbers) { + if (globalOptions["parse-numbers"]) { // going to assume evaluate expression is processed first, THEN the result is parsed normally - if (globalOptions.evaluateExpression) { + if (globalOptions["evaluate-expression"]) { // TODO Sanitize Evaluate Expression! let expression = globalOptions.expression expression = expression.replace('#1', text); - let result = eval(expression); - text = result.toString(); + text = eval(expression).toString(); } const numlist = parseList(parser, text, globalOptions); - if (globalOptions.listExponents === 'individual') { + if (globalOptions["list-exponents"] === 'individual') { numlist.forEach(v => { - postProcessNumber(v, globalOptions); + postProcessNumber(parser, v, globalOptions); }); } else { const targetExponent = numlist[0].exponentSign + numlist[0].exponent; const altOptions = Object.assign(globalOptions, { exponentMode: 'fixed', fixedExponent: targetExponent }); numlist.forEach((v, i) => { - if (i == 0) { - postProcessNumber(v, globalOptions); + if (i === 0) { + postProcessNumber(parser, v, globalOptions); } else { - postProcessNumber(v, altOptions); + postProcessNumber(parser, v, altOptions); } }); } const mapItem = listNumberMap.get(numlist.length) ?? listNumberMap.get(3); - const mmlNodes = mapItem(numlist, parser, globalOptions); - mmlNodes.forEach(v => { - parser.Push(v); - }); + const mmlNode = mapItem(numlist, parser, globalOptions); + parser.Push(mmlNode); + } else { const mml = (new TexParser(text, parser.stack.env, parser.configuration)).mml(); diff --git a/src/numproductMethods.ts b/src/numproductMethods.ts index ba8187c..9caea5a 100644 --- a/src/numproductMethods.ts +++ b/src/numproductMethods.ts @@ -3,86 +3,84 @@ import { IOptions, findOptions } from "./options/options"; import { INumberPiece, parseNumber } from "./numMethods"; import { postProcessNumber } from "./numPostProcessMethods"; import { MmlNode } from "mathjax-full/js/core/MmlTree/MmlNode"; -import { createExponentMml, displayOutputMml } from "./numDisplayMethods"; -import { ExponentsMode } from "./options/listOptions"; +import { displayOutputMml } from "./numDisplayMethods"; import { exponentListModeMap } from "./numlistMethods"; -const listNumberMap = new MapMmlNode[]>([ +const listNumberMap = new MapMmlNode>([ [1, (nums: INumberPiece[], parser: TexParser, options: IOptions) => { return displayOutputMml(nums[0], parser, options); }], [3, (nums: INumberPiece[], parser: TexParser, options: IOptions) => { - const exponentMapItem = exponentListModeMap.get(options.listExponents); + const exponentMapItem = exponentListModeMap.get(options["list-exponents"]); const exponentResult = exponentMapItem(nums, parser, options); - let total = []; + const root = parser.create('node', 'inferredMrow', [], {}); if (exponentResult.leading){ - total.push(exponentResult.leading); + root.appendChild(exponentResult.leading); } - total = total.concat(displayOutputMml(exponentResult.numbers[0], parser, options)); + root.appendChild(displayOutputMml(exponentResult.numbers[0], parser, options)); for (let i=1; i< nums.length; i++){ - const separator = (new TexParser(options.productMode === 'symbol' ? options.productSymbol : `\\text{${options.productPhrase}}`, parser.stack.env, parser.configuration)).mml(); + const separator = (new TexParser(options["product-mode"] === 'symbol' ? options["product-symbol"] : `\\text{${options["product-phrase"]}}`, parser.stack.env, parser.configuration)).mml(); const next = displayOutputMml(exponentResult.numbers[i], parser, options); - total = total.concat(separator).concat(next); + root.appendChild(separator); + root.appendChild(next); } if (exponentResult.trailing){ - total = total.concat(exponentResult.trailing); + root.appendChild(exponentResult.trailing); } - return total; + return root; }] ]); export function parseProductList(parser:TexParser, input : string, options:IOptions): INumberPiece[] { const values = input.split('x'); const nums = values.map(v=>{ - return parseNumber(parser, v, options); + return parseNumber(parser, v.trim(), options); }); return nums; } export function processNumberProduct(parser: TexParser): void { - const globalOptions: IOptions = { ...parser.options as IOptions }; + const globalOptions: IOptions = { ...parser.options.siunitx as IOptions }; - const localOptions = findOptions(parser); + const localOptions = findOptions(parser, globalOptions); Object.assign(globalOptions, localOptions); let text = parser.GetArgument('num'); - if (globalOptions.parseNumbers) { + if (globalOptions["parse-numbers"]) { // going to assume evaluate expression is processed first, THEN the result is parsed normally - if (globalOptions.evaluateExpression) { + if (globalOptions["evaluate-expression"]) { // TODO Sanitize Evaluate Expression! let expression = globalOptions.expression expression = expression.replace('#1', text); - let result = eval(expression); + const result = eval(expression); text = result.toString(); } const numlist = parseProductList(parser, text, globalOptions); - if (globalOptions.productExponents === 'individual'){ + if (globalOptions["product-exponents"] === 'individual'){ numlist.forEach(v=>{ - postProcessNumber(v, globalOptions); + postProcessNumber(parser, v, globalOptions); }); } else { const targetExponent = numlist[0].exponentSign + numlist[0].exponent; const altOptions = Object.assign(globalOptions, { exponentMode: 'fixed', fixedExponent: targetExponent }); numlist.forEach((v,i)=>{ - if (i == 0){ - postProcessNumber(v, globalOptions); + if (i === 0){ + postProcessNumber(parser, v, globalOptions); } else { - postProcessNumber(v, altOptions); + postProcessNumber(parser, v, altOptions); } }); } const mapItem = listNumberMap.get(numlist.length) ?? listNumberMap.get(3); - const mmlNodes = mapItem(numlist, parser, globalOptions); - mmlNodes.forEach(v=>{ - parser.Push(v); - }); - + const mmlNode = mapItem(numlist, parser, globalOptions); + parser.Push(mmlNode); + } else { const mml = (new TexParser(text, parser.stack.env, parser.configuration)).mml(); parser.Push(mml); diff --git a/src/numrangeMethods.ts b/src/numrangeMethods.ts index 46ed2ee..2006245 100644 --- a/src/numrangeMethods.ts +++ b/src/numrangeMethods.ts @@ -6,33 +6,33 @@ import { displayOutputMml } from "./numDisplayMethods"; import { exponentListModeMap } from "./numlistMethods"; export function processNumberRange(parser: TexParser): void { - const globalOptions: IOptions = { ...parser.options as IOptions }; + const globalOptions: IOptions = { ...parser.options.siunitx as IOptions }; - const localOptions = findOptions(parser); + const localOptions = findOptions(parser, globalOptions); Object.assign(globalOptions, localOptions); - let first = parser.GetArgument('firstNum'); - let last = parser.GetArgument('lastNum'); + const first = parser.GetArgument('firstNum'); + const last = parser.GetArgument('lastNum'); - if (globalOptions.parseNumbers) { + if (globalOptions["parse-numbers"]) { const firstNum = parseNumber(parser, first, globalOptions); const lastNum = parseNumber(parser, last, globalOptions); - if (globalOptions.rangeExponents === 'individual'){ - postProcessNumber(firstNum, globalOptions); - postProcessNumber(lastNum, globalOptions); + if (globalOptions["range-exponents"] === 'individual'){ + postProcessNumber(parser, firstNum, globalOptions); + postProcessNumber(parser, lastNum, globalOptions); } else { const targetExponent = firstNum.exponentSign + firstNum.exponent; const altOptions = Object.assign(globalOptions, { exponentMode: 'fixed', fixedExponent: targetExponent }); - postProcessNumber(firstNum, globalOptions); - postProcessNumber(lastNum, altOptions); + postProcessNumber(parser, firstNum, globalOptions); + postProcessNumber(parser, lastNum, altOptions); } - const exponentMapItem = exponentListModeMap.get(globalOptions.rangeExponents); + const exponentMapItem = exponentListModeMap.get(globalOptions["range-exponents"]); const exponentResult = exponentMapItem([firstNum, lastNum], parser, globalOptions); const firstMml = displayOutputMml(exponentResult.numbers[0], parser, globalOptions); - const separator = (new TexParser(`\\text{${globalOptions.rangePhrase}}`, parser.stack.env, parser.configuration)).mml(); + const separator = (new TexParser(`\\text{${globalOptions["range-phrase"]}}`, parser.stack.env, parser.configuration)).mml(); const lastMml = displayOutputMml(exponentResult.numbers[1], parser, globalOptions); let total = []; if (exponentResult.leading){ diff --git a/src/options/angleOptions.ts b/src/options/angleOptions.ts index dcc7e4e..f66c4f0 100644 --- a/src/options/angleOptions.ts +++ b/src/options/angleOptions.ts @@ -4,28 +4,28 @@ type AngleMode = 'input' | 'arc' | 'decimal'; // since angles use the same system number processing system, it extends the INumOptions export interface IAngleOptions extends INumOptions { - angleMode: AngleMode; - angleSymbolDegree: string; - angleSymbolMinute: string; - angleSymbolOverDecimal: boolean; - angleSymbolSecond: string; - angleSeparator: string; - fillAngleDegrees: boolean; - fillAngleMinutes: boolean; - fillAngleSeconds: boolean; - numberAngleProduct: string; + "angle-mode": AngleMode; + "angle-symbol-degree": string; + "angle-symbol-minute": string; + "angle-symbol-over-decimal": boolean; + "angle-symbol-second": string; + "angle-separator": string; + "fill-angle-degrees": boolean; + "fill-angle-minutes": boolean; + "fill-angle-seconds": boolean; + "number-angle-product": string; } export const AngleOptionDefaults: IAngleOptions = { ...NumOptionDefaults, - angleMode: 'input', - angleSymbolDegree: '\\degree', - angleSymbolMinute: "'", //'\\arcminute', - angleSymbolOverDecimal: false, - angleSymbolSecond: "''",//'\\arcsecond', - angleSeparator: '', - fillAngleDegrees: false, - fillAngleMinutes: false, - fillAngleSeconds: false, - numberAngleProduct: '' + "angle-mode": 'input', + "angle-symbol-degree": '\\degree', + "angle-symbol-minute": "'", //'\\arcminute', + "angle-symbol-over-decimal": false, + "angle-symbol-second": "''",//'\\arcsecond', + "angle-separator": '', + "fill-angle-degrees": false, + "fill-angle-minutes": false, + "fill-angle-seconds": false, + "number-angle-product": '' } diff --git a/src/options/complexNumberOptions.ts b/src/options/complexNumberOptions.ts index fa56251..de93174 100644 --- a/src/options/complexNumberOptions.ts +++ b/src/options/complexNumberOptions.ts @@ -1,21 +1,21 @@ export interface IComplexNumberOptions { - complexAngleUnit: 'degrees' | 'radians'; - complexMode: 'input' | 'cartesian' | 'polar'; - complexRootPosition: 'before-number' | 'after-number'; - complexSymbolAngle: string; - complexSymbolDegree: string; - inputComplexRoot: string; - outputComplexRoot: string; - printComplexUnity: boolean; + "complex-angle-unit": 'degrees' | 'radians'; + "complex-mode": 'input' | 'cartesian' | 'polar'; + "complex-root-position": 'before-number' | 'after-number'; + "complex-symbol-angle": string; + "complex-symbol-degree": string; + "input-complex-root": string; + "output-complex-root": string; + "print-complex-unity": boolean; } export const ComplexNumberOptionsDefault: IComplexNumberOptions = { - complexAngleUnit: 'degrees', - complexMode: 'input', - complexRootPosition: 'after-number', - complexSymbolAngle: '\\angle', - complexSymbolDegree: '\\degree', - inputComplexRoot: 'ij', - outputComplexRoot: '\\mathrm{i}', - printComplexUnity: false + "complex-angle-unit": 'degrees', + "complex-mode": 'input', + "complex-root-position": 'after-number', + "complex-symbol-angle": '\\angle', + "complex-symbol-degree": '\\degree', + "input-complex-root": 'ij', + "output-complex-root": '\\mathrm{i}', + "print-complex-unity": false } \ No newline at end of file diff --git a/src/options/listOptions.ts b/src/options/listOptions.ts index cac11e3..44d5cbc 100644 --- a/src/options/listOptions.ts +++ b/src/options/listOptions.ts @@ -1,53 +1,49 @@ -import { MmlNode } from "mathjax-full/js/core/MmlTree/MmlNode"; -import TexParser from "mathjax-full/js/input/tex/TexParser"; -import { IOptions, findOptions } from "./options"; - export type ExponentsMode = 'individual' | 'combine-bracket' | 'combine'; type UnitsMode = 'repeat' | 'bracket' | 'single'; export type UnitsModeProduct = UnitsMode | 'bracket-power' | 'power'; -export interface IListOptions{ - listCloseBracket: string; - listOpenBracket: string; - listExponents: ExponentsMode; - listFinalSeparator: string; - listPairSeparator: string; - listSeparator: string; - listUnits: UnitsMode; - productCloseBracket: string; - productOpenBracket: string; - productExponents: ExponentsMode; - productMode:'symbol'|'phrase'; - productPhrase: string; - productSymbol: string; - productUnits: UnitsModeProduct; - rangeCloseBracket: string; - rangeOpenBracket: string; - rangeExponents: ExponentsMode; - rangePhrase: string; - rangeUnits: UnitsMode; - +export interface IListOptions { + "list-close-bracket": string; + "list-open-bracket": string; + "list-exponents": ExponentsMode; + "list-final-separator": string; + "list-pair-separator": string; + "list-separator": string; + "list-units": UnitsMode; + "product-close-bracket": string; + "product-open-bracket": string; + "product-exponents": ExponentsMode; + "product-mode": 'symbol' | 'phrase'; + "product-phrase": string; + "product-symbol": string; + "product-units": UnitsModeProduct; + "range-close-bracket": string; + "range-open-bracket": string; + "range-exponents": ExponentsMode; + "range-phrase": string; + "range-units": UnitsMode; + } export const ListOptionDefaults: IListOptions = { - listCloseBracket: ')', - listOpenBracket: '(', - listExponents: 'individual', - listFinalSeparator: ', and ', - listPairSeparator: ' and ', - listSeparator: ', ', - listUnits: 'repeat', - productCloseBracket: ')', - productOpenBracket: '(', - productExponents: 'individual', - productMode: 'symbol', - productPhrase: ' by ', - productSymbol: '\\times', - productUnits: 'repeat', - rangeCloseBracket: ')', - rangeOpenBracket: '(', - rangeExponents: 'individual', - rangePhrase: ' to ', - rangeUnits: 'repeat' + "list-close-bracket": ')', + "list-open-bracket": '(', + "list-exponents": 'individual', + "list-final-separator": ', and ', + "list-pair-separator": ' and ', + "list-separator": ', ', + "list-units": 'repeat', + "product-close-bracket": ')', + "product-open-bracket": '(', + "product-exponents": 'individual', + "product-mode": 'symbol', + "product-phrase": ' by ', + "product-symbol": '\\times', + "product-units": 'repeat', + "range-close-bracket": ')', + "range-open-bracket": '(', + "range-exponents": 'individual', + "range-phrase": ' to ', + "range-units": 'repeat' } diff --git a/src/options/numberOptions.ts b/src/options/numberOptions.ts index 069bcb0..9100f3d 100644 --- a/src/options/numberOptions.ts +++ b/src/options/numberOptions.ts @@ -5,138 +5,138 @@ type UncertaintyMode = 'separate' | 'compact' | 'full' | 'compact-marker'; type UncertaintyDescriptorMode = 'bracket' | 'bracket-separator' | 'separator' | 'subscript'; export interface INumParseOptions { - evaluateExpression: boolean; // not implemented, requires library math parser + "evaluate-expression": boolean; // not implemented, requires library math parser expression: string; // not implemented, requires library math parser - inputCloseUncertainty: string; - inputComparators: string; - inputDecimalMarkers: string; - inputDigits: string; - inputExponentMarkers: string; - inputIgnore: string; - inputOpenUncertainty: string; - inputSigns: string; - inputUncertaintySigns: string; - parseNumbers: boolean; - retainExplicitDecimalMarker: boolean; - retainExplicitPlus: boolean; - retainNegativeZero: boolean; - retainZeroUncertainty: boolean; + "input-close-uncertainty": string; + "input-comparators": string; + "input-decimal-markers": string; + "input-digits": string; + "input-exponent-markers": string; + "input-ignore": string; + "input-open-uncertainty": string; + "input-signs": string; + "input-uncertainty-signs": string; + "parse-numbers": boolean; + "retain-explicit-decimal-marker": boolean; + "retain-explicit-plus": boolean; + "retain-negative-zero": boolean; + "retain-zero-uncertainty": boolean; } export interface INumPostOptions { - dropExponent: boolean; - dropUncertainty: boolean; - dropZeroDecimal: boolean; - exponentMode: ExponentMode; - exponentThresholds: string; - fixedExponent: number; - minimumIntegerDigits: number; - minimumDecimalDigits: number; - roundHalf: 'up' | 'even'; - roundMinimum: string; - roundMode: RoundMode; - roundPad: boolean; - roundPrecision: number; - roundZeroPositive: boolean; + "drop-exponent": boolean; + "drop-uncertainty": boolean; + "drop-zero-decimal": boolean; + "exponent-mode": ExponentMode; + "exponent-thresholds": string; + "fixed-exponent": number; + "minimum-integer-digits": number; + "minimum-decimal-digits": number; + "round-half": 'up' | 'even'; + "round-minimum": string; + "round-mode": RoundMode; + "round-pad": boolean; + "round-precision": number; + "round-zero-positive": boolean; } export interface INumOutputOptions { - bracketAmbiguousNumbers: boolean; // TODO: (bracketAmbiguousNumbers) not implemented yet - bracketNegativeNumbers: boolean; - digitGroupSize: number; - digitGroupFirstSize: number; - digitGroupOtherSize: number; - exponentBase: string; - exponentProduct: string; - groupDigits: GroupDigits; - groupMinimumDigits: number; - groupSeparator: string; // can be LaTeX spacers, but Unicode is better! - negativeColor: string; - outputCloseUncertainty: string; - outputDecimalMarker: string; - outputExponentMarker: string; - outputOpenUncertainty: string; - printImplicitPlus: boolean; - printUnityMantissa: boolean; - printZeroExponent: boolean; - printZeroInteger: boolean; - tightSpacing: boolean; // TODO: not implemented - uncertaintyDescriptorMode: UncertaintyDescriptorMode; // TODO: not implemented - uncertaintyDescriptorSeparator: string; // TODO: not implemented - uncertaintyDescriptors: string; // TODO: not implemented - uncertaintyMode: UncertaintyMode; - uncertaintySeparator: string; - zeroDecimalAsSymbol: boolean; - zeroSymbol: string; + "bracket-ambiguous-numbers": boolean; // TODO: (bracketAmbiguousNumbers) not implemented yet + "bracket-negative-numbers": boolean; + "digit-group-size": number; + "digit-group-first-size": number; + "digit-group-other-size": number; + "exponent-base": string; + "exponent-product": string; + "group-digits": GroupDigits; + "group-minimum-digits": number; + "group-separator": string; // can be LaTeX spacers, but Unicode is better! + "negative-color": string; + "output-close-uncertainty": string; + "output-decimal-marker": string; + "output-exponent-marker": string; + "output-open-uncertainty": string; + "print-implicit-plus": boolean; + "print-unity-mantissa": boolean; + "print-zero-exponent": boolean; + "print-zero-integer": boolean; + "tight-spacing": boolean; // TODO: not implemented + "uncertainty-descriptor-mode": UncertaintyDescriptorMode; // TODO: not implemented + "uncertainty-descriptor-separator": string; // TODO: not implemented + "uncertainty-descriptors": string; // TODO: not implemented + "uncertainty-mode": UncertaintyMode; + "uncertainty-separator": string; + "zero-decimal-as-symbol": boolean; + "zero-symbol": string; } export interface INumOptions extends INumParseOptions, INumPostOptions, INumOutputOptions { } export const NumParseOptionDefaults: INumParseOptions = { - evaluateExpression: false, + "evaluate-expression": false, expression: '#1', - inputCloseUncertainty: ')', - inputComparators: '<=>\\approx\\ge\\geq\\gg\\le\\leq\\ll\\sim', - inputDecimalMarkers: '.,', - inputDigits: '0123456789', - inputExponentMarkers: 'dDeE', - inputIgnore: '', - inputOpenUncertainty: '(', - inputSigns: '+-\\pm\\mp', // currently using a hack to differentiate between \\pm sign vs uncertaintysign - inputUncertaintySigns: '\\pm\\mp', - parseNumbers: true, - retainExplicitDecimalMarker: false, - retainExplicitPlus: false, - retainNegativeZero: false, - retainZeroUncertainty: false + "input-close-uncertainty": ')', + "input-comparators": '<=>\\approx\\ge\\geq\\gg\\le\\leq\\ll\\sim', + "input-decimal-markers": '.,', + "input-digits": '0123456789', + "input-exponent-markers": 'dDeE', + "input-ignore": '', + "input-open-uncertainty": '(', + "input-signs": '+-\\pm\\mp', // currently using a hack to differentiate between \\pm sign vs uncertaintysign + "input-uncertainty-signs": '\\pm\\mp', + "parse-numbers": true, + "retain-explicit-decimal-marker": false, + "retain-explicit-plus": false, + "retain-negative-zero": false, + "retain-zero-uncertainty": false }; export const NumPostOptionDefaults: INumPostOptions = { - dropExponent: false, - dropUncertainty: false, - dropZeroDecimal: false, - exponentMode: 'input', - exponentThresholds: '-3:3', - fixedExponent: 0, - minimumIntegerDigits: 0, - minimumDecimalDigits: 0, - roundHalf: 'up', - roundMinimum: '0', - roundMode: 'none', - roundPad: true, - roundPrecision: 2, - roundZeroPositive: true + "drop-exponent": false, + "drop-uncertainty": false, + "drop-zero-decimal": false, + "exponent-mode": 'input', + "exponent-thresholds": '-3:3', + "fixed-exponent": 0, + "minimum-integer-digits": 0, + "minimum-decimal-digits": 0, + "round-half": 'up', + "round-minimum": '0', + "round-mode": 'none', + "round-pad": true, + "round-precision": 2, + "round-zero-positive": true }; export const NumOutputOptionDefaults: INumOutputOptions = { - bracketAmbiguousNumbers: true, - bracketNegativeNumbers: false, - digitGroupSize: 3, - digitGroupFirstSize: -1, // These should be -1 so we can detect when they've been explicitly set. - digitGroupOtherSize: -1, // Otherwise, digitGroupSize will override them. - exponentBase: '10', - exponentProduct: '\\times', - groupDigits: 'all', - groupMinimumDigits: 5, - groupSeparator: '\\,', - negativeColor: '', - outputCloseUncertainty: ')', - outputDecimalMarker: '.', - outputExponentMarker: '', - outputOpenUncertainty: '(', - printImplicitPlus: false, - printUnityMantissa: true, - printZeroExponent: false, - printZeroInteger: true, - tightSpacing: false, - uncertaintyDescriptorMode: 'bracket-separator', - uncertaintyDescriptorSeparator: '\\', - uncertaintyDescriptors: '', - uncertaintyMode: 'compact', - uncertaintySeparator: '', - zeroDecimalAsSymbol: false, - zeroSymbol: '\\mbox{---}' + "bracket-ambiguous-numbers": true, + "bracket-negative-numbers": false, + "digit-group-size": 3, + "digit-group-first-size": -1, // These should be -1 so we can detect when they've been explicitly set. + "digit-group-other-size": -1, // Otherwise, digitGroupSize will override them. + "exponent-base": '10', + "exponent-product": '\\times', + "group-digits": 'all', + "group-minimum-digits": 5, + "group-separator": '\\,', + "negative-color": '', + "output-close-uncertainty": ')', + "output-decimal-marker": '.', + "output-exponent-marker": '', + "output-open-uncertainty": '(', + "print-implicit-plus": false, + "print-unity-mantissa": true, + "print-zero-exponent": false, + "print-zero-integer": true, + "tight-spacing": false, + "uncertainty-descriptor-mode": 'bracket-separator', + "uncertainty-descriptor-separator": '\\', + "uncertainty-descriptors": '', + "uncertainty-mode": 'compact', + "uncertainty-separator": '', + "zero-decimal-as-symbol": false, + "zero-symbol": '\\mbox{---}' } export const NumOptionDefaults: INumOptions = { ...NumParseOptionDefaults, ...NumPostOptionDefaults, ...NumOutputOptionDefaults }; diff --git a/src/options/options.ts b/src/options/options.ts index 90ee6ce..55c8487 100644 --- a/src/options/options.ts +++ b/src/options/options.ts @@ -1,6 +1,5 @@ -import TexError from "mathjax-full/js/input/tex/TexError"; import TexParser from "mathjax-full/js/input/tex/TexParser"; -import { defaultOptions } from "mathjax-full/js/util/Options"; +import ParseUtil from 'mathjax-full/js/input/tex/ParseUtil'; import { IUnitOptions, UnitOptionDefaults } from "./unitOptions"; import { INumOptions, NumOptionDefaults } from "./numberOptions"; import { IAngleOptions, AngleOptionDefaults } from "./angleOptions"; @@ -8,63 +7,50 @@ import { IQuantityOptions, QuantityOptionDefaults } from "./quantityOptions"; import { IPrintOptions, PrintOptionsDefault } from "./printOptions"; import { IComplexNumberOptions, ComplexNumberOptionsDefault } from "./complexNumberOptions"; import { IListOptions, ListOptionDefaults } from "./listOptions"; +import { siunitxError } from "../error/errors"; +import "./patch.js"; +import { EnvList } from "mathjax-full/js/input/tex/StackItem"; export interface IOptions extends IUnitOptions, INumOptions, IAngleOptions, IQuantityOptions, IComplexNumberOptions, IPrintOptions, IListOptions { } export const siunitxDefaults = { - ...UnitOptionDefaults, - ...NumOptionDefaults, - ...AngleOptionDefaults, - ...QuantityOptionDefaults, - ...ComplexNumberOptionsDefault, + ...UnitOptionDefaults, + ...NumOptionDefaults, + ...AngleOptionDefaults, + ...QuantityOptionDefaults, + ...ComplexNumberOptionsDefault, ...PrintOptionsDefault, ...ListOptionDefaults }; -// Needed a new version of TexParser.GetBrackets because it wanted to parse the internal macros automatically. -// This method just gets the bracketed option string only. -export function findOptions(parser: TexParser): Partial { - - if (parser.GetNext() !== '[') { - return {}; - } - const j = ++parser.i; - let depth = 0; - while (parser.i < parser.string.length) { - if (parser.string.charAt(parser.i) == '{') depth++; - else if (parser.string.charAt(parser.i) == '}') depth--; - else if (parser.string.charAt(parser.i) == ']' && depth == 0) { - const result = parser.string.slice(j, parser.i); - parser.i++; - const options = optionStringToObject(result); - return options; - } - parser.i++; - } - throw new TexError('MissingCloseBracket', - 'Could not find closing \']\' for argument to %1', parser.currentCS); +// originally this function contained a manual version of getting options inside brackets... not necessary anymore +export function findOptions(parser: TexParser, globalOptions: IOptions): Partial { + // No good way to extend typing for patch + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return optionStringToObject((parser as any).GetBrackets(parser.currentCS, undefined, true), globalOptions); } -// from https://stackoverflow.com/a/10425344/1938624 -function dashToCamel(input: string): string { - return input.toLowerCase().replace(/-(.)/g, (match, group) => { - return group.toUpperCase(); - }); -} +// // from https://stackoverflow.com/a/10425344/1938624 +// function dashToCamel(input: string): string { +// return input.toLowerCase().replace(/-(.)/g, (match, group) => { +// return group.toUpperCase(); +// }); +// } -// from https://stackoverflow.com/a/47932848/1938624 -function camelToDash(str: string): string { - return str.replace(/([A-Z])/g, ($1) => { return "-" + $1.toLowerCase(); }); -} +// // from https://stackoverflow.com/a/47932848/1938624 +// // eslint-disable-next-line @typescript-eslint/no-unused-vars +// function camelToDash(str: string): string { +// return str.replace(/([A-Z])/g, ($1) => { return "-" + $1.toLowerCase(); }); +// } export function processSISetup(parser: TexParser): void { - let globalOptions: IOptions = { ...parser.options as IOptions }; + const globalOptions: IOptions = { ...parser.options.siunitx as IOptions }; const optionsString = parser.GetArgument('sisetup'); const options = processOptions(globalOptions, optionsString); - options.forEach((v, k) => parser.options[k] = v); + Object.assign(parser.options.siunitx, options); // We are adding the sisetup options to the parser options. These are global once the page is loaded. // (the globalOptions variable is just a copy and will reset between each siunitx command) @@ -74,202 +60,43 @@ export function processSISetup(parser: TexParser): void { } -function optionStringToObject( optionString: string):Partial{ - const options : Partial = {}; - if (optionString != null) { - // check if wrapped in curly braces and remove them - while (optionString.startsWith('{') && optionString.endsWith('}')) { - optionString = optionString.slice(1, optionString.length - 1); - } - let prop = ''; - let onValue = false; - let depth = 0; - let escaped = false; - let value = ''; - for (const c of optionString) { - if (c == '{') { - if (onValue) { - value += c; - } else { - prop += c; - } - depth++; - } - else if (c == '}') { - depth--; - if (onValue) { - value += c; - } else { - prop += c; - } - } - else if (c == '\\') { - escaped = true; - if (onValue) { - value += c; - } else { - prop += c; - } - } - else if (c == ',' && depth == 0 && !escaped) { - prop = dashToCamel(prop.trim()); - if (value == '') { - options[prop] = true; - } - else if (typeof siunitxDefaults[prop] === 'number') { - options[prop] = +(value.trim()); - } else if (typeof siunitxDefaults[prop] === 'boolean') { - options[prop] = (value.trim() === 'true'); - } else { - if (value.indexOf('\\') == -1) { - value = value.trim(); - // finally, remove curly brackets around value if present - value = value.replace(/^{(.*)}$/g, '$1'); - } - options[prop] = value; - } - prop = ''; - value = ''; - onValue = false; - } - else if (c == '=' && depth == 0) { - onValue = true; - } - else { - if (onValue) { - if (c == ' ' && escaped) { - escaped = false; - } - value += c; - } else { - prop += c; - } - } - } - prop = dashToCamel(prop.trim()); - - if (value == '') { - options[prop] = true; - } - else if (typeof siunitxDefaults[prop] === 'number') { - options[prop] = +(value.trim()); - } else if (typeof siunitxDefaults[prop] === 'boolean') { - options[prop] = (value.trim() === 'true'); - } else { - if (value.indexOf('\\') == -1) { - value = value.trim(); - // finally, remove curly brackets around value if present - value = value.replace(/^{(.*)}$/g, '$1'); +function optionStringToObject(optionString: string, globalOptions: IOptions): Partial { + // No good way to extend typing for patch + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const optionObject: EnvList = (ParseUtil.keyvalOptions as any)(optionString, globalOptions as unknown as { [key: string]: number }, true, true); + const options: Partial = {}; + for (let [key, value] of Object.entries(optionObject)) { + const type = typeof globalOptions[key]; + if (typeof value !== type) { + if (type === 'number' && value.toString().match(/^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[-+]\d+)?$/)) { + value = parseFloat(value.toString()); + } else { + throw siunitxError.InvalidOptionValue(key, type); } - options[prop] = value; } + options[key] = value; } + return options; } // LaTeX commands (in the value portion) MUST end with a space before using a comma to add another option -export function processOptions(globalOptions: IOptions, optionString: string): Map { - const options = new Map(); - - if (optionString != null) { - // check if wrapped in curly braces and remove them - while (optionString.startsWith('{') && optionString.endsWith('}')) { - optionString = optionString.slice(1, optionString.length - 1); - } - let prop = ''; - let onValue = false; - let depth = 0; - let escaped = false; - let value = ''; - for (const c of optionString) { - if (c == '{') { - if (onValue) { - value += c; - } else { - prop += c; - } - depth++; - } - else if (c == '}') { - depth--; - if (onValue) { - value += c; - } else { - prop += c; - } - } - else if (c == '\\') { - escaped = true; - if (onValue) { - value += c; - } else { - prop += c; - } - } - else if (c == ',' && depth == 0 && !escaped) { - prop = dashToCamel(prop.trim()); - //console.log(prop + ': ' + value); - if (value == '') { - //globalOptions[prop] = true; - options.set(prop, true); - } - else if (typeof globalOptions[prop] === 'number') { - //globalOptions[prop] = +(value.trim()); - options.set(prop, +(value.trim())); - } else if (typeof globalOptions[prop] === 'boolean') { - //globalOptions[prop] = (value.trim() === 'true'); - options.set(prop, (value.trim() === 'true')); - } else { - if (value.indexOf('\\') == -1) { - value = value.trim(); - // finally, remove curly brackets around value if present - value = value.replace(/^{(.*)}$/g, '$1'); - } - //globalOptions[prop] = value; - options.set(prop, value); - } - prop = ''; - value = ''; - onValue = false; - } - else if (c == '=' && depth == 0) { - onValue = true; +export function processOptions(globalOptions: IOptions, optionString: string): Record { + // No good way to extend typing for patch + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const optionObject : EnvList = (ParseUtil.keyvalOptions as any)(optionString, globalOptions as unknown as { [key: string]: number }, true, true) ; + const options = {}; + + for (let [key, value] of Object.entries(optionObject)) { + const type = typeof globalOptions[key]; + if (typeof value !== type) { + if (type === 'number' && value.toString().match(/^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[-+]\d+)?$/)) { + value = parseFloat(value.toString()); + } else { + throw siunitxError.InvalidOptionValue(key, type); } - else { - if (onValue) { - if (c == ' ' && escaped) { - escaped = false; - } - value += c; - } else { - prop += c; - } - } - } - - prop = dashToCamel(prop.trim()); - //console.log(prop + ': ' + value); - if (value == '') { - //globalOptions[prop] = true; - options.set(prop, true); - } - else if (typeof globalOptions[prop] === 'number') { - //globalOptions[prop] = +(value.trim()); - options.set(prop, +(value.trim())); - } else if (typeof globalOptions[prop] === 'boolean') { - //globalOptions[prop] = (value.trim() === 'true'); - options.set(prop, (value.trim() === 'true')); - } else { - if (value.indexOf('\\') == -1) { - value = value.trim(); - // finally, remove curly brackets around value if present - value = value.replace(/^{(.*)}$/g, '$1'); - } - //globalOptions[prop] = value; - options.set(prop, value); } + options[key] = value; } - return options; - } \ No newline at end of file diff --git a/src/options/patch.ts b/src/options/patch.ts new file mode 100644 index 0000000..c0be170 --- /dev/null +++ b/src/options/patch.ts @@ -0,0 +1,189 @@ +import TexError from 'mathjax-full/js/input/tex/TexError'; +import { EnvList } from 'mathjax-full/js/input/tex/StackItem'; +import TexParser from 'mathjax-full/js/input/tex/TexParser'; +import ParseUtil from 'mathjax-full/js/input/tex/ParseUtil'; + + + + +/** + * Splits a package option list of the form [x=y,z=1] into an attribute list + * of the form {x: y, z: 1}. + * @param {string} attrib The attributes of the package. + * @param {{[key: string]: number}?} allowed A list of allowed options. If + * given only allowed arguments are returned. + * @param {boolean?} error If true, raises an exception if not allowed options + * are found. + * @param {boolean?} l3keys If true, use l3key-style parsing (only remove one set of braces) + * @return {EnvList} The attribute list. + */ +ParseUtil.keyvalOptions = function ( + attrib: string, + allowed: { [key: string]: number } = null, + error: boolean = false, + l3keys: boolean = false +): EnvList { + const def: EnvList = readKeyval(attrib, l3keys); + if (allowed) { + for (const key of Object.keys(def)) { + // eslint-disable-next-line no-prototype-builtins + if (!allowed.hasOwnProperty(key)) { + if (error) { + throw new TexError('InvalidOption', 'Invalid option: %1', key); + } + delete def[key]; + } + } + } + return def; +} + + +/** + * Implementation of the keyval function from https://www.ctan.org/pkg/keyval + * @param {string} text The optional parameter string for a package or + * command. + * @param {boolean?} l3keys If true, use l3key-style parsing (only remove one set of braces) + * @return {EnvList} Set of options as key/value pairs. + */ +function readKeyval(text: string, l3keys: boolean = false): EnvList { + const options: EnvList = {}; + let rest = text; + let end, key, val; + let dropBrace = true; + while (rest) { + [key, end, rest] = readValue(rest, ['=', ','], l3keys, dropBrace); + dropBrace = false; + if (end === '=') { + [val, end, rest] = readValue(rest, [','], l3keys); + val = (val === 'false' || val === 'true') ? + JSON.parse(val) : val; + options[key] = val; + } else if (key) { + options[key] = true; + } + } + return options; +} + + +/** + * Removes pairs of outer braces. + * @param {string} text The string to clean. + * @param {number} count The number of outer braces to slice off. + * @return {string} The cleaned string. + */ +function removeBraces(text: string, count: number): string { + if (count === 0) { + return text.replace(/^\s+/, '') + .replace(/([^\\\s]|^)((?:\\\\)*(?:\\\s)?)?\s+$/, '$1$2'); + } + while (count > 0) { + text = text.trim().slice(1, -1); + count--; + } + return text; +} + + +/** + * Read a value from the given string until an end parameter is reached or + * string is exhausted. + * @param {string} text The string to process. + * @param {string[]} end List of possible end characters. + * @param {boolean?} l3keys If true, use l3key-style parsing (only remove one set of braces) + * @param {boolean?} dropBrace True if the outermost braces should be dropped + * @return {[string, string, string]} The collected value, the actual end + * character, and the rest of the string still to parse. + */ +function readValue( + text: string, + end: string[], + l3keys: boolean = false, + dropBrace: boolean = false +): [string, string, string] { + const length = text.length; + let braces = 0; + let value = ''; + let index = 0; + let start = 0; // Counter for the starting left braces. + let countBraces = true; // Flag for counting starting left braces. + // after starting braces, but no other char yet. + while (index < length) { + const c = text[index++]; + switch (c) { + case '\\': // Handle control sequences (in particular, \{ and \}) + value += c + (text[index++] || ''); + countBraces = false; + continue; + case ' ': // Ignore spaces. + break; + case '{': + if (countBraces) { // Count open left braces at start. + start++; + } + braces++; + break; + case '}': + if (!braces) { // Closing braces. + throw new TexError('ExtraCloseMissingOpen', 'Extra close brace or missing open brace'); + } + braces--; + countBraces = false; // Stop counting start left braces. + break; + default: + if (!braces && end.indexOf(c) !== -1) { // End character reached. + return [removeBraces(value, l3keys ? Math.min(1, start) : start), c, text.slice(index)]; + } + if (start > braces) { // Some start left braces have been closed. + start = braces; + } + countBraces = false; + } + value += c; + } + if (braces) { + throw new TexError('ExtraOpenMissingClose', 'Extra open brace or missing close brace'); + } + return (dropBrace && start) ? ['', '', removeBraces(value, 1)] : + [removeBraces(value, l3keys ? Math.min(1, start) : start), '', text.slice(index)]; +} + + +/** + * Get an optional LaTeX argument in brackets. + * @param {string} _name Name of the current control sequence. + * @param {string?} def The default value for the optional argument. + * @param {boolean=} matchBrackets True if indernal brackets must match. + * @return {string} The optional argument. + */ +TexParser.prototype.GetBrackets = function (_name: string, def?: string, matchBrackets: boolean = false): string { + if (this.GetNext() !== '[') { + return def; + } + // eslint-disable-next-line prefer-const + let j = ++this.i, braces = 0, brackets = 0; + while (this.i < this.string.length) { + switch (this.string.charAt(this.i++)) { + case '{': braces++; break; + case '\\': this.i++; break; + case '}': + if (braces-- <= 0) { + throw new TexError('ExtraCloseLooking', + 'Extra close brace while looking for %1', '\']\''); + } + break; + case '[': if (braces === 0) brackets++; break; + case ']': + if (braces === 0) { + if (!matchBrackets || brackets === 0) { + return this.string.slice(j, this.i - 1); + } + brackets--; + } + break; + } + } + throw new TexError('MissingCloseBracket', + 'Could not find closing \']\' for argument to %1', this.currentCS); +} diff --git a/src/options/printOptions.ts b/src/options/printOptions.ts index 0ebbf14..ea5d4ed 100644 --- a/src/options/printOptions.ts +++ b/src/options/printOptions.ts @@ -18,15 +18,15 @@ export interface IPrintOptions { textSeriesToMath: boolean; // not implemented // WARNING: using MathJax, \\color{blue} will only color everything past that point until the END of the LINE. - color: string; // not implemented - numberColor: string; // not implemented - unitColor: string; // not implemented + color: string; + "number-color": string; + "unit-color": string; } export const PrintOptionsDefault: IPrintOptions = { color: '', mode: 'math', - numberColor: '', + "number-color": '', numberMode: 'math', propagateMathFont: false, resetMathVersion: true, @@ -38,6 +38,6 @@ export const PrintOptionsDefault: IPrintOptions = { textSubscriptCommand: '\\textsubscript', textSuperscriptCommand: '\\textsuperscript', textSeriesToMath: false, - unitColor: '', + "unit-color": '', unitMode: 'math' } \ No newline at end of file diff --git a/src/options/quantityOptions.ts b/src/options/quantityOptions.ts index 64d2187..ccbd89b 100644 --- a/src/options/quantityOptions.ts +++ b/src/options/quantityOptions.ts @@ -5,19 +5,19 @@ export type PrefixMode = 'input' | 'combine-exponent' | 'extract-exponent'; export type SeparateUncertaintyUnits = 'bracket' | 'repeat' | 'single'; export interface IQuantityOptions extends INumOptions, IUnitOptions { - allowQuantityBreaks: boolean; // not implemented, // TODO: allowQuantityBreaks: Check that this can't really be done with MathJax - extractMassInKilograms: boolean; - prefixMode: PrefixMode; - quantityProduct: '\\,'; - separateUncertaintyUnits: SeparateUncertaintyUnits; + "allow-quantity-breaks": boolean; // not implemented, // TODO: allowQuantityBreaks: Check that this can't really be done with MathJax + "extract-mass-in-kilograms": boolean; + "prefix-mode": PrefixMode; + "quantity-product": '\\,'; + "separate-uncertainty-units": SeparateUncertaintyUnits; } export const QuantityOptionDefaults: IQuantityOptions = { ...NumOptionDefaults, ...UnitOptionDefaults, - allowQuantityBreaks: false, - extractMassInKilograms: true, - prefixMode: 'input', - quantityProduct: '\\,', - separateUncertaintyUnits: 'bracket' + "allow-quantity-breaks": false, + "extract-mass-in-kilograms": true, + "prefix-mode": 'input', + "quantity-product": '\\,', + "separate-uncertainty-units": 'bracket' } \ No newline at end of file diff --git a/src/options/unitOptions.ts b/src/options/unitOptions.ts index c362c15..4eeef58 100644 --- a/src/options/unitOptions.ts +++ b/src/options/unitOptions.ts @@ -2,37 +2,37 @@ type PerMode = 'power' | 'fraction' | 'symbol' | 'power-positive-first' | 'repea export type QualifierMode = 'subscript' | 'bracket' | 'combine' | 'phrase'; export interface IUnitOptions { - interUnitProduct: string; - perMode: PerMode; - displayPerMode: PerMode; // not implemented, global setting only - inlinePerMode: PerMode; // not implemented, global setting only - perSymbol: string; - fractionCommand: string; - bracketUnitDenominator: boolean; - perSymbolScriptCorrection: string; - stickyPer: boolean; - qualifierMode: QualifierMode; - qualifierPhrase: string; - powerHalfAsSqrt: boolean; - parseUnits: boolean; - forbidLiteralUnits: boolean; - unitFontCommand: string; + "inter-unit-product": string; + "per-mode": PerMode; + "display-per-mode": PerMode; // not implemented, global setting only + "inline-per-mode": PerMode; // not implemented, global setting only + "per-symbol": string; + "fraction-command": string; + "bracket-unit-denominator": boolean; + "per-symbol-script-correction": string; + "sticky-per": boolean; + "qualifier-mode": QualifierMode; + "qualifier-phrase": string; + "power-half-as-sqrt": boolean; + "parse-units": boolean; + "forbid-literal-units": boolean; + "unit-font-command": string; } export const UnitOptionDefaults: IUnitOptions = { - bracketUnitDenominator: true, - forbidLiteralUnits: false, - fractionCommand: '\\frac', - interUnitProduct: '\\,', - parseUnits: true, - perMode: 'power', - displayPerMode: 'perMode', - inlinePerMode: 'perMode', - perSymbolScriptCorrection: '\\!', - perSymbol: '/', - powerHalfAsSqrt: false, - qualifierMode: 'subscript', - qualifierPhrase: '', - stickyPer: false, - unitFontCommand: '\\mathrm' + "bracket-unit-denominator": true, + "forbid-literal-units": false, + "fraction-command": '\\frac', + "inter-unit-product": '\\,', + "parse-units": true, + "per-mode": 'power', + "display-per-mode": 'perMode', + "inline-per-mode": 'perMode', + "per-symbol-script-correction": '\\!', + "per-symbol": '/', + "power-half-as-sqrt": false, + "qualifier-mode": 'subscript', + "qualifier-phrase": '', + "sticky-per": false, + "unit-font-command": '\\mathrm' } \ No newline at end of file diff --git a/src/qtyMethods.ts b/src/qtyMethods.ts index baa413e..b5b13b0 100644 --- a/src/qtyMethods.ts +++ b/src/qtyMethods.ts @@ -1,5 +1,5 @@ import TexParser from "mathjax-full/js/input/tex/TexParser"; -import { displayOutputMml, findInnerText, spacerMap } from "./numDisplayMethods"; +import { displayOutputMml, findInnerText } from "./numDisplayMethods"; import { INumberPiece, parseNumber } from "./numMethods"; import { convertToFixed, postProcessNumber } from "./numPostProcessMethods"; import { findOptions, IOptions } from "./options/options"; @@ -7,10 +7,9 @@ import { IQuantityOptions, PrefixMode, SeparateUncertaintyUnits } from "./option import { displayUnits, IUnitPiece, parseUnit } from "./unitMethods"; import { prefixPower } from "./units"; import { MmlNode } from "mathjax-full/js/core/MmlTree/MmlNode"; -import { GlobalParser } from "./siunitx"; -function combineExponent(num: INumberPiece, units: IUnitPiece[], options: IQuantityOptions): void { - if (num.exponent == '' || (units == null || units.length == 0)) { +function combineExponent(parser: TexParser, num: INumberPiece, units: IUnitPiece[], options: IQuantityOptions): void { + if (!num.exponent || (!units || units.length === 0)) { return; } @@ -25,8 +24,9 @@ function combineExponent(num: INumberPiece, units: IUnitPiece[], options: IQuant } const firstUnit = units[0]; - if (firstUnit.prefix != '') { - const unitPower = (firstUnit.power != null ? +(firstUnit.power) : 1) * (firstUnit.position == 'denominator' ? -1 : 1); + // prefix can be undefined, empty, or string... this specifically checks for empty + if (firstUnit.prefix !== '') { + const unitPower = (firstUnit.power !== null ? +(firstUnit.power) : 1) * (firstUnit.position === 'denominator' ? -1 : 1); const addedPower = firstUnit.prefix ? prefixPower.get(firstUnit.prefix) : 1; targetExponent += addedPower * unitPower; // just in case prefix was cm (2) and we added 3, there's no prefix for 5 @@ -43,14 +43,13 @@ function combineExponent(num: INumberPiece, units: IUnitPiece[], options: IQuant const newExponent = targetExponent - exponent; num.exponent = (Math.abs(newExponent)).toString(); num.exponentSign = Math.sign(newExponent) > 0 ? '' : '-'; - convertToFixed(num, options); + convertToFixed(parser, num, options); } -function extractExponent(num: INumberPiece, units: IUnitPiece[], options: IQuantityOptions): void { - if (units == null) { +function extractExponent(parser: TexParser, num: INumberPiece, units: IUnitPiece[], options: IQuantityOptions): void { + if (units === null) { return; } - let powersOfTen = 0; //let powersOfTwo = 0; @@ -61,8 +60,8 @@ function extractExponent(num: INumberPiece, units: IUnitPiece[], options: IQuant // 2. prefix is present for grams when !extractMassInKilograms // 2. prefix is not k for grams when extractMassInKilograms // special case - if ((unit.symbol !== 'g' && unit.prefix !== '') || (unit.symbol === 'g' && unit.prefix !== '' && !options.extractMassInKilograms)) { - const unitPower = (unit.power != null ? +(unit.power) : 1) * (unit.position == 'denominator' ? -1 : 1); + if ((unit.symbol !== 'g' && unit.prefix !== '') || (unit.symbol === 'g' && unit.prefix !== '' && !options["extract-mass-in-kilograms"])) { + const unitPower = ((unit.power !== undefined && unit.power !== null) ? +(unit.power) : 1) * (unit.position === 'denominator' ? -1 : 1); // if (binaryPrefixPower.has(unit.prefix)){ // const prefPower = binaryPrefixPower.get(unit.prefix); // powersOfTwo += (prefPower*unitPower); @@ -75,8 +74,8 @@ function extractExponent(num: INumberPiece, units: IUnitPiece[], options: IQuant continue; } unit.prefix = ''; - } else if (unit.symbol === 'g' && unit.prefix !== 'k' && options.extractMassInKilograms) { - const unitPower = (unit.power != null ? +(unit.power) : 1) * (unit.position == 'denominator' ? -1 : 1); + } else if (unit.symbol === 'g' && unit.prefix !== 'k' && options["extract-mass-in-kilograms"]) { + const unitPower = ((unit.power !== undefined && unit.power !== null) ? +(unit.power) : 1) * (unit.position === 'denominator' ? -1 : 1); if (prefixPower.has(unit.prefix)) { const prefPower = prefixPower.get(unit.prefix); powersOfTen += (prefPower * unitPower) - 3; @@ -87,16 +86,16 @@ function extractExponent(num: INumberPiece, units: IUnitPiece[], options: IQuant unit.prefix = 'k'; } } - const currentExponent = (num.exponent != '' ? +(num.exponentSign + num.exponent) : 0); + const currentExponent = (num.exponent !== '' ? +(num.exponentSign + num.exponent) : 0); const newExponent = currentExponent + powersOfTen; num.exponent = Math.abs(newExponent).toString(); num.exponentSign = Math.sign(newExponent) > 0 ? '' : '-'; - if (num.exponentMarker == '') { + if (!num.exponentMarker) { num.exponentMarker = 'e'; } } -export const prefixModeMap = new Map void>([ +export const prefixModeMap = new Map void>([ // eslint-disable-next-line @typescript-eslint/no-empty-function ['input', (): void => { }], ['combine-exponent', combineExponent], @@ -107,14 +106,14 @@ function findUncertaintyNode(root: MmlNode): MmlNode | null { for (const x of root.childNodes) { const mmlNode = x as MmlNode; if (mmlNode) { - if (mmlNode.attributes != null) { + if (mmlNode.attributes !== null) { const names = mmlNode.attributes.getExplicitNames(); - if (names.indexOf('data-siunitx-uncertainty') != -1) { + if (names.indexOf('data-siunitx-uncertainty') !== -1) { return mmlNode; } } const result = findUncertaintyNode(mmlNode); - if (result != null) { + if (result !== null) { return result; } } @@ -122,105 +121,126 @@ function findUncertaintyNode(root: MmlNode): MmlNode | null { return null; } -const separateUncertaintyUnitsMmlMap = new Map MmlNode[]>([ - ['single', (num: MmlNode[], units: MmlNode, quantityProduct: MmlNode, parser: TexParser, options: IQuantityOptions): MmlNode[] => { - - return [...num, quantityProduct, units]; +const separateUncertaintyUnitsMmlMap = new Map MmlNode>([ + ['single', (num: MmlNode, units: MmlNode, quantityProduct: MmlNode, parser: TexParser, _options: IQuantityOptions): MmlNode => { + const root = parser.create('node', 'inferredMrow', [], {}); + root.appendChild(num); + root.appendChild(quantityProduct); + root.appendChild(units); + return root; }], - ['bracket', (num: MmlNode[], units: MmlNode, quantityProduct: MmlNode, parser: TexParser, options: IQuantityOptions): MmlNode[] => { + ['bracket', (num: MmlNode, units: MmlNode, quantityProduct: MmlNode, parser: TexParser, options: IQuantityOptions): MmlNode => { + const root = parser.create('node', 'inferredMrow', [], {}); let uncertaintyNode: MmlNode = null; - for (const x of num) { - const result = findUncertaintyNode(x); - if (result != null) { + for (const x of num.childNodes) { + const result = findUncertaintyNode(x as MmlNode); + if (result !== null) { uncertaintyNode = result; break; } } - if (uncertaintyNode != null) { - const leftBracket = parser.create('token', 'mo', {}, options.outputOpenUncertainty); - const rightBracket = parser.create('token', 'mo', {}, options.outputCloseUncertainty); - return [leftBracket, ...num, rightBracket, quantityProduct, units]; + if (uncertaintyNode !== null) { + const leftBracket = parser.create('token', 'mo', {}, options["output-open-uncertainty"]); + const rightBracket = parser.create('token', 'mo', {}, options["output-close-uncertainty"]); + root.appendChild(leftBracket); + root.appendChild(num); + root.appendChild(rightBracket); + root.appendChild(quantityProduct); + root.appendChild(units); + return root; } else { - return [...num, quantityProduct, units]; + root.appendChild(num); + root.appendChild(quantityProduct); + root.appendChild(units); + return root; } }], - ['repeat', (num: MmlNode[], units: MmlNode, quantityProduct: MmlNode, parser: TexParser, options: IQuantityOptions): MmlNode[] => { + ['repeat', (num: MmlNode, units: MmlNode, quantityProduct: MmlNode, parser: TexParser, _options: IQuantityOptions): MmlNode => { let uncertaintyNode: MmlNode = null; - for (const x of num) { - const result = findUncertaintyNode(x); - if (result != null) { + for (const x of num.childNodes) { + const result = findUncertaintyNode(x as MmlNode); + if (result !== null) { uncertaintyNode = result; break; } } - if (uncertaintyNode != null) { + if (uncertaintyNode !== null) { const parent = uncertaintyNode.parent; const uncertaintyPosition = parent.childNodes.indexOf(uncertaintyNode); - parent.childNodes.splice(uncertaintyPosition, 0, quantityProduct, units); + if (!quantityProduct) { + + parent.childNodes.splice(uncertaintyPosition, 0, units); + parent.appendChild(units); + } else { - // To make it match the MathML structure of the previous insert, - // we should insert the 2nd unit at the same depth. - // However, SRE seems to rearrange it all anyways. - parent.appendChild(quantityProduct); - parent.appendChild(units); + parent.childNodes.splice(uncertaintyPosition, 0, quantityProduct, units); - return [...num]; + // To make it match the MathML structure of the previous insert, + // we should insert the 2nd unit at the same depth. + // However, SRE seems to rearrange it all anyways. + + parent.appendChild(quantityProduct); + parent.appendChild(units); + + } + return num; } else { - return [...num, quantityProduct, units]; + const root = parser.create('node', 'inferredMrow', [], {}); + root.appendChild(num); + root.appendChild(quantityProduct); + root.appendChild(units); + return root; } }] ]); const separateUncertaintyUnitsMap = new Map string>([ ['single', (num: string, units: string, options: IQuantityOptions): string => { - return num + options.quantityProduct + units; + return num + options["quantity-product"] + units; }], ['bracket', (num: string, units: string, options: IQuantityOptions): string => { - if (num.indexOf('\\pm') == -1) { - return num + options.quantityProduct + units; + if (num.indexOf('\\pm') === -1) { + return num + options["quantity-product"] + units; } - return options.outputOpenUncertainty + num + options.outputCloseUncertainty + options.quantityProduct + units; + return options["output-open-uncertainty"] + num + options["output-close-uncertainty"] + options["quantity-product"] + units; }], ['repeat', (num: string, units: string, options: IQuantityOptions): string => { // split the num from the uncertainty, split on \\pm const split = num.split('\\pm'); let separate = ''; for (let i = 0; i < split.length; i++) { - if (separate != '') { + if (separate !== '') { separate += '\\pm'; } separate += split[i]; - separate += options.quantityProduct; + separate += options["quantity-product"]; separate += units; } return separate; }] ]); -export function createQuantityProductMml(parser:TexParser, options:IOptions):MmlNode|null{ +export function createQuantityProductMml(parser: TexParser, options: IOptions): MmlNode | null { let quantityProductNode = null; - const trimmedQuantityProduct = options.quantityProduct.trimStart(); - if (trimmedQuantityProduct !== '') { - let quantityProduct = spacerMap[trimmedQuantityProduct]; - if (quantityProduct === undefined) { - // instead of copying quantityProduct, - // should auto parse latex and extract unicode from mml - const spacerNode = (new TexParser(quantityProduct, GlobalParser.stack.env, GlobalParser.configuration)).mml(); - quantityProduct = findInnerText(spacerNode); - } - quantityProductNode = parser.create('token', 'mo', {}, quantityProduct); - } + + const trimmedQuantityProduct = options["quantity-product"].trimStart(); + if (trimmedQuantityProduct) { + const spacerNode = (new TexParser(trimmedQuantityProduct, parser.stack.env, parser.configuration)).mml(); + const spacerUnicode = findInnerText(spacerNode); + quantityProductNode = parser.create('token', 'mo', {}, spacerUnicode); + } else { + quantityProductNode = parser.create('token', 'mo', {} ); + } return quantityProductNode; } export function processQuantity(parser: TexParser): void { - let globalOptions: IOptions = { ...parser.options as IOptions }; + let globalOptions: IOptions = { ...parser.options.siunitx as IOptions }; - const localOptions = findOptions(parser); - //const localOptions = optionStringToObject(localOptionString); + const localOptions = findOptions(parser, globalOptions); let numString = parser.GetArgument('num'); const unitString = parser.GetArgument('unit'); @@ -228,22 +248,21 @@ export function processQuantity(parser: TexParser): void { let unitDisplay = ''; - const isLiteral = (unitString.indexOf('\\') == -1); + const isLiteral = (unitString.indexOf('\\') === -1); const unitPieces = parseUnit(parser, unitString, globalOptions, localOptions, isLiteral); - if (globalOptions.parseNumbers) { + if (globalOptions["parse-numbers"]) { // going to assume evaluate expression is processed first, THEN the result is parsed normally - if (globalOptions.evaluateExpression) { + if (globalOptions["evaluate-expression"]) { // TODO Sanitize Evaluate Expression! let expression = globalOptions.expression expression = expression.replace('#1', numString); - let result = eval(expression); - numString = result.toString(); + numString = eval(expression).toString(); } // refresh global options from default - globalOptions = { ...parser.options as IOptions }; + globalOptions = { ...parser.options.siunitx as IOptions }; //processOptions(globalOptions, localOptionString); //const options = processOptions(globalOptions, localOptions); //options.forEach((v, k) => globalOptions[k] = v); @@ -253,10 +272,10 @@ export function processQuantity(parser: TexParser): void { //console.log(JSON.parse(JSON.stringify(unitPieces))); // convert number and unit if necessary - prefixModeMap.get(globalOptions.prefixMode)?.(num, unitPieces, globalOptions); + prefixModeMap.get(globalOptions["prefix-mode"])?.(parser, num, unitPieces, globalOptions); //console.log(JSON.parse(JSON.stringify(unitPieces))); - postProcessNumber(num, globalOptions); + postProcessNumber(parser, num, globalOptions); //console.log(JSON.parse(JSON.stringify(unitPieces))); @@ -266,28 +285,10 @@ export function processQuantity(parser: TexParser): void { unitDisplay = displayUnits(parser, unitPieces, globalOptions, isLiteral); const unitNode = (new TexParser(unitDisplay, parser.stack.env, parser.configuration)).mml(); - // for (const num of numDisplay){ - // parser.Push(num); - // } - // parser.Push(unitNode); - // uncertainty will already be separated. - let quantityProductNode = createQuantityProductMml(parser, globalOptions); - // const trimmedQuantityProduct = globalOptions.quantityProduct.trimStart(); - // if (trimmedQuantityProduct !== '') { - // let quantityProduct = spacerMap[trimmedQuantityProduct]; - // if (quantityProduct === undefined) { - // // instead of copying quantityProduct, - // // should auto parse latex and extract unicode from mml - // const spacerNode = (new TexParser(quantityProduct, GlobalParser.stack.env, GlobalParser.configuration)).mml(); - // quantityProduct = findInnerText(spacerNode); - // } - // quantityProductNode = parser.create('token', 'mo', {}, quantityProduct); - // } - - const qtyDisplay = separateUncertaintyUnitsMmlMap.get(globalOptions.separateUncertaintyUnits)(numDisplay, unitNode, quantityProductNode, parser, globalOptions); - for (const piece of qtyDisplay) { - parser.Push(piece); - } + const quantityProductNode = createQuantityProductMml(parser, globalOptions); + + const qtyDisplay = separateUncertaintyUnitsMmlMap.get(globalOptions["separate-uncertainty-units"])(numDisplay, unitNode, quantityProductNode, parser, globalOptions); + parser.Push(qtyDisplay); } else { // can't do any conversions with number since processing is off @@ -296,7 +297,7 @@ export function processQuantity(parser: TexParser): void { // Need to process this after number because some options alter unit prefixes unitDisplay = displayUnits(parser, unitPieces, globalOptions, isLiteral); - const qtyDisplay = separateUncertaintyUnitsMap.get(globalOptions.separateUncertaintyUnits)(numDisplay, unitDisplay, globalOptions); + const qtyDisplay = separateUncertaintyUnitsMap.get(globalOptions["separate-uncertainty-units"])(numDisplay, unitDisplay, globalOptions); const qtyNode = (new TexParser(qtyDisplay, parser.stack.env, parser.configuration)).mml(); parser.Push(qtyNode); } diff --git a/src/qtylistMethods.ts b/src/qtylistMethods.ts index 27ee27a..3a1c4fc 100644 --- a/src/qtylistMethods.ts +++ b/src/qtylistMethods.ts @@ -1,62 +1,70 @@ import TexParser from "mathjax-full/js/input/tex/TexParser"; import { IOptions, findOptions } from "./options/options"; -import { INumberPiece, parseNumber } from "./numMethods"; +import { INumberPiece } from "./numMethods"; import { postProcessNumber } from "./numPostProcessMethods"; import { MmlNode } from "mathjax-full/js/core/MmlTree/MmlNode"; -import { createExponentMml, displayOutputMml } from "./numDisplayMethods"; -import { ExponentsMode, UnitsModeProduct } from "./options/listOptions"; +import { displayOutputMml } from "./numDisplayMethods"; +import { UnitsModeProduct } from "./options/listOptions"; import { IExponentModeOutput, bracketCloseMap, bracketOpenMap, exponentListModeMap, parseList } from "./numlistMethods"; import { displayUnits, parseUnit } from "./unitMethods"; -import { createQuantityProductMml, prefixModeMap } from "./qtyMethods"; +import { createQuantityProductMml } from "./qtyMethods"; interface IUnitsModeOutput { leading?: MmlNode; - numbers: MmlNode[][]; - trailing?: MmlNode[]; + numbers: MmlNode[]; + trailing?: MmlNode; } -function singleExponent(exponentResult: IExponentModeOutput, unitNodes: MmlNode[], parser:TexParser, options: IOptions):IUnitsModeOutput { +function singleExponent(exponentResult: IExponentModeOutput, unitNode: MmlNode, parser:TexParser, options: IOptions):IUnitsModeOutput { const numNodes = exponentResult.numbers.map(v=>{ const numNode = displayOutputMml(v, parser, options); return numNode; }); if (exponentResult.trailing){ - exponentResult.trailing = exponentResult.trailing.concat(unitNodes); + exponentResult.trailing.appendChild(unitNode); } else { - exponentResult.trailing = []; - exponentResult.trailing = exponentResult.trailing.concat(unitNodes); + exponentResult.trailing = parser.create('node', 'inferredMrow', [], {}); + exponentResult.trailing.appendChild(unitNode); } return { numbers: numNodes, leading: exponentResult.leading, trailing: exponentResult.trailing }; } -function bracketExponent(exponentResult: IExponentModeOutput, unitNodes: MmlNode[], parser:TexParser, options: IOptions): IUnitsModeOutput{ +function bracketExponent(exponentResult: IExponentModeOutput, unitNode: MmlNode, parser:TexParser, options: IOptions): IUnitsModeOutput{ const numNodes = exponentResult.numbers.map(v=>{ const numNode = displayOutputMml(v, parser, options); return numNode; }); - if (exponentResult.leading === undefined){ + if (!exponentResult.leading){ + exponentResult.leading = parser.create('node', 'inferredMrow', [], {}); const leadingBracket = (new TexParser(bracketOpenMap.get(parser.currentCS)(options), parser.stack.env, parser.configuration)).mml(); - exponentResult.leading = leadingBracket; + exponentResult.leading.appendChild(leadingBracket); } - if (options.listExponents !== 'combine-bracket'){ + if (!exponentResult.trailing){ + exponentResult.trailing = parser.create('node', 'inferredMrow', [], {}); + } + if (options["list-exponents"] !== 'combine-bracket'){ const trailingBracket = (new TexParser(bracketCloseMap.get(parser.currentCS)(options), parser.stack.env, parser.configuration)).mml(); - if (options.listExponents === 'individual'){ - exponentResult.trailing = []; + if (options["list-exponents"] === 'individual'){ + //override collection of exponents by clearing them out + exponentResult.trailing = parser.create('node', 'inferredMrow', [], {}); } - exponentResult.trailing.push(trailingBracket); + exponentResult.trailing.appendChild(trailingBracket); } - exponentResult.trailing = exponentResult.trailing.concat(unitNodes); + exponentResult.trailing.appendChild(unitNode); return { numbers: numNodes, leading: exponentResult.leading, trailing: exponentResult.trailing }; } -export const unitListModeMap = new MapIUnitsModeOutput>([ - ['repeat', (exponentResult: IExponentModeOutput, unitNodes: MmlNode[], parser:TexParser, options: IOptions)=>{ +export const unitListModeMap = new MapIUnitsModeOutput>([ + ['repeat', (exponentResult: IExponentModeOutput, unitNode: MmlNode, parser:TexParser, options: IOptions)=>{ const numNodes = exponentResult.numbers.map(v=>{ + const root = parser.create('node', 'inferredMrow', [], {}); const numNode = displayOutputMml(v, parser, options); - return numNode.concat(unitNodes); + root.appendChild(numNode); + root.appendChild(unitNode); + return root; }); return { numbers: numNodes, leading: exponentResult.leading, trailing: exponentResult.trailing }; }], @@ -66,135 +74,121 @@ export const unitListModeMap = new MapMmlNode[]>([ - [1, (nums: INumberPiece[], unitNodes: MmlNode[], parser: TexParser, options: IOptions) => { +const listNumberMap = new MapMmlNode>([ + [1, (nums: INumberPiece[], unitNode: MmlNode, parser: TexParser, options: IOptions) => { + const root = parser.create('node', 'inferredMrow', [], {}); const numMml= displayOutputMml(nums[0], parser, options); - numMml.concat(unitNodes); - return numMml; + root.appendChild(numMml); + root.appendChild(unitNode); + return root; }], - [2, (nums: INumberPiece[], unitNodes: MmlNode[], parser: TexParser, options: IOptions) => { - - const exponentMapItem = exponentListModeMap.get(options.listExponents); + [2, (nums: INumberPiece[], unitNode: MmlNode, parser: TexParser, options: IOptions) => { + const exponentMapItem = exponentListModeMap.get(options["list-exponents"]); const exponentResult = exponentMapItem(nums, parser, options); - const unitsMapItem = unitListModeMap.get(options.listUnits); - const unitsResult = unitsMapItem(exponentResult, unitNodes, parser,options); + const unitsMapItem = unitListModeMap.get(options["list-units"]); + const unitsResult = unitsMapItem(exponentResult, unitNode, parser,options); - let total = []; + const root = parser.create('node', 'inferredMrow', [], {}); if (unitsResult.leading){ - total.push(unitsResult.leading); + root.appendChild(unitsResult.leading); } - //const first = displayOutputMml(exponentResult.numbers[0], parser, options); - total = total.concat(unitsResult[0]); - // if (options.listUnits === 'single'){ - // total.push(unitNode); - // } - const separator = (new TexParser(`\\text{${options.listPairSeparator}}`, parser.stack.env, parser.configuration)).mml(); - total = total.concat(separator); - //const second = displayOutputMml(exponentResult.numbers[1], parser, options); - total = total.concat(unitsResult[1]); - // if (options.listUnits === 'single'){ - // total.push(unitNode); - // } - + root.appendChild(unitsResult.numbers[0]); + const separator = (new TexParser(`\\text{${options["list-pair-separator"]}}`, parser.stack.env, parser.configuration)).mml(); + root.appendChild(separator); + root.appendChild(unitsResult.numbers[1]); + if (unitsResult.trailing){ - total = total.concat(unitsResult.trailing); + root.appendChild(unitsResult.trailing); } - return total; + return root; }], - [3, (nums: INumberPiece[], unitNodes: MmlNode[], parser: TexParser, options: IOptions) => { - const exponentMapItem = exponentListModeMap.get(options.listUnits === 'single' ? 'individual' : options.listExponents); + [3, (nums: INumberPiece[], unitNode: MmlNode, parser: TexParser, options: IOptions) => { + const exponentMapItem = exponentListModeMap.get(options["list-units"] === 'single' ? 'individual' : options["list-exponents"]); const exponentResult = exponentMapItem(nums, parser, options); - const unitsMapItem = unitListModeMap.get(options.listUnits); - const unitsResult = unitsMapItem(exponentResult, unitNodes, parser,options); + const unitsMapItem = unitListModeMap.get(options["list-units"]); + const unitsResult = unitsMapItem(exponentResult, unitNode, parser,options); - let total = []; + const root = parser.create('node', 'inferredMrow', [], {}); if (unitsResult.leading){ - total.push(unitsResult.leading); + root.appendChild(unitsResult.leading); } - // total = total.concat(displayOutputMml(exponentResult.numbers[0], parser, options)); - total = total.concat(unitsResult.numbers[0]); - // if (options.listUnits === 'repeat'){ - // total.push(unitNode); - // } - for (let i=1; i< nums.length-1; i++){ - const separator = (new TexParser(`\\text{${options.listSeparator}}`, parser.stack.env, parser.configuration)).mml(); - //const next = displayOutputMml(exponentResult.numbers[i], parser, options); - total = total.concat(separator).concat(unitsResult.numbers[i]); - // if (options.listUnits === 'repeat'){ - // total.push(unitNode); - // } + + root.appendChild(unitsResult.numbers[0]); + + const separator = (new TexParser(`\\text{${options["list-separator"]}}`, parser.stack.env, parser.configuration)).mml(); + for (let i=1; i< nums.length-1; i++){ + root.appendChild(separator); + root.appendChild(unitsResult.numbers[i]); } - const finalSeparator = (new TexParser(`\\text{${options.listFinalSeparator}}`, parser.stack.env, parser.configuration)).mml(); - //const last = displayOutputMml(exponentResult.numbers[exponentResult.numbers.length-1], parser, options); - total = total.concat(finalSeparator).concat(unitsResult.numbers[unitsResult.numbers.length-1]); - // if (options.listUnits === 'repeat'){ - // total.push(unitNode); - // } + const finalSeparator = (new TexParser(`\\text{${options["list-final-separator"]}}`, parser.stack.env, parser.configuration)).mml(); + root.appendChild(finalSeparator); + root.appendChild(unitsResult.numbers[unitsResult.numbers.length-1]); + if (unitsResult.trailing){ - total = total.concat(unitsResult.trailing); + root.appendChild(unitsResult.trailing); } - return total; + return root; }] ]); export function processQuantityList(parser: TexParser): void { - const globalOptions: IOptions = { ...parser.options as IOptions }; + const globalOptions: IOptions = { ...parser.options.siunitx as IOptions }; - const localOptions = findOptions(parser); + const localOptions = findOptions(parser, globalOptions); Object.assign(globalOptions, localOptions); - let text = parser.GetArgument('num'); - let unitString = parser.GetArgument('unit'); - const isLiteral = (unitString.indexOf('\\') == -1); + const text = parser.GetArgument('num'); + const unitString = parser.GetArgument('unit'); + const isLiteral = (unitString.indexOf('\\') === -1); const unitPieces = parseUnit(parser, unitString, globalOptions, localOptions, isLiteral); - if (globalOptions.parseNumbers) { + if (globalOptions["parse-numbers"]) { const numlist = parseList(parser, text, globalOptions); // // convert number and unit if necessary, use first number in list only // prefixModeMap.get(globalOptions.prefixMode)?.(numlist[0], unitPieces, globalOptions); // list-units=repeat requires list-exponents=individual, so override if necessary - if (globalOptions.listUnits === 'repeat'){ - globalOptions.listExponents = 'individual'; + if (globalOptions["list-units"] === 'repeat'){ + globalOptions["list-exponents"] = 'individual'; } // seems both have to be set to have the list printed plainly, any changes in units display will affect exponents display and v.v. - if (globalOptions.listExponents === 'individual'){ + if (globalOptions["list-exponents"] === 'individual'){ numlist.forEach(v=>{ - postProcessNumber(v, globalOptions); + postProcessNumber(parser, v, globalOptions); }); } else { const targetExponent = numlist[0].exponentSign + numlist[0].exponent; const altOptions = Object.assign(globalOptions, { exponentMode: 'fixed', fixedExponent: targetExponent }); numlist.forEach((v,i)=>{ - if (i == 0){ - postProcessNumber(v, globalOptions); + if (i === 0){ + postProcessNumber(parser, v, globalOptions); } else { - postProcessNumber(v, altOptions); + postProcessNumber(parser, v, altOptions); } }); } - // Need to process this after number because some options alter unit prefixes - let unitDisplay = displayUnits(parser, unitPieces, globalOptions, isLiteral); - const unitNode = [(new TexParser(unitDisplay, parser.stack.env, parser.configuration)).mml()]; + // Need to process this after number because some options alter unit prefixes + const unitDisplay = displayUnits(parser, unitPieces, globalOptions, isLiteral); + let unitNode = (new TexParser(unitDisplay, parser.stack.env, parser.configuration)).mml(); const quantityProductNode = createQuantityProductMml(parser, globalOptions); if (quantityProductNode){ - unitNode.splice(0,0,quantityProductNode); + const root = parser.create('node', 'inferredMrow', [], {}); + root.appendChild(quantityProductNode); + root.appendChild(unitNode); + unitNode = root; } const mapItem = listNumberMap.get(numlist.length) ?? listNumberMap.get(3); - const mmlNodes = mapItem(numlist, unitNode, parser, globalOptions); - mmlNodes.forEach(v=>{ - parser.Push(v); - }); + parser.Push(mapItem(numlist, unitNode, parser, globalOptions)); } else { const mml = (new TexParser(text, parser.stack.env, parser.configuration)).mml(); diff --git a/src/qtyproductMethods.ts b/src/qtyproductMethods.ts index 843f11a..497e4ad 100644 --- a/src/qtyproductMethods.ts +++ b/src/qtyproductMethods.ts @@ -1,10 +1,9 @@ import TexParser from "mathjax-full/js/input/tex/TexParser"; import { IOptions, findOptions } from "./options/options"; -import { INumberPiece, parseNumber } from "./numMethods"; +import { INumberPiece } from "./numMethods"; import { postProcessNumber } from "./numPostProcessMethods"; import { MmlNode } from "mathjax-full/js/core/MmlTree/MmlNode"; -import { createExponentMml, displayOutputMml } from "./numDisplayMethods"; -import { ExponentsMode } from "./options/listOptions"; +import { displayOutputMml } from "./numDisplayMethods"; import { exponentListModeMap } from "./numlistMethods"; import { parseProductList } from "./numproductMethods"; import { displayUnits, parseUnit } from "./unitMethods"; @@ -12,78 +11,81 @@ import { createQuantityProductMml } from "./qtyMethods"; import { unitListModeMap } from "./qtylistMethods"; -const listNumberMap = new MapMmlNode[]>([ - [1, (nums: INumberPiece[], unitNodes: MmlNode[],parser: TexParser, options: IOptions) => { - const nodes= displayOutputMml(nums[0], parser, options); - return nodes.concat(unitNodes); +const listNumberMap = new MapMmlNode>([ + [1, (nums: INumberPiece[], unitNode: MmlNode,parser: TexParser, options: IOptions) => { + const root = parser.create('node', 'inferredMrow', [], {}); + const node = displayOutputMml(nums[0], parser, options); + root.appendChild(node); + root.appendChild(unitNode); + return root; }], - [3, (nums: INumberPiece[], unitNodes: MmlNode[], parser: TexParser, options: IOptions) => { - const exponentMapItem = exponentListModeMap.get(options.listExponents); + [3, (nums: INumberPiece[], unitNode: MmlNode, parser: TexParser, options: IOptions) => { + const exponentMapItem = exponentListModeMap.get(options["list-exponents"]); const exponentResult = exponentMapItem(nums, parser, options); - const unitsMapItem = unitListModeMap.get(options.productUnits); - const unitsResult = unitsMapItem(exponentResult, unitNodes, parser,options); + const unitsMapItem = unitListModeMap.get(options["product-units"]); + const unitsResult = unitsMapItem(exponentResult, unitNode, parser,options); - let total = []; + const root = parser.create('node', 'inferredMrow', [], {}); if (unitsResult.leading){ - total.push(unitsResult.leading); + root.appendChild(unitsResult.leading); } //total = total.concat(displayOutputMml(exponentResult.numbers[0], parser, options)); - total = total.concat(unitsResult.numbers[0]); + root.appendChild(unitsResult.numbers[0]); for (let i=1; i< nums.length; i++){ - const separator = (new TexParser(options.productMode === 'symbol' ? options.productSymbol : `\\text{${options.productPhrase}}`, parser.stack.env, parser.configuration)).mml(); + const separator = (new TexParser(options["product-mode"] === 'symbol' ? options["product-symbol"] : `\\text{${options["product-phrase"]}}`, parser.stack.env, parser.configuration)).mml(); //const next = displayOutputMml(exponentResult.numbers[i], parser, options); - total = total.concat(separator).concat(unitsResult.numbers[i]); + root.appendChild(separator); + root.appendChild(unitsResult.numbers[i]); } if (unitsResult.trailing){ - total = total.concat(unitsResult.trailing); + root.appendChild(unitsResult.trailing); } - return total; + return root; }] ]); export function processQuantityProduct(parser: TexParser): void { - const globalOptions: IOptions = { ...parser.options as IOptions }; + const globalOptions: IOptions = { ...parser.options.siunitx as IOptions }; - const localOptions = findOptions(parser); + const localOptions = findOptions(parser, globalOptions); Object.assign(globalOptions, localOptions); let text = parser.GetArgument('num'); - let unitString = parser.GetArgument('unit'); - const isLiteral = (unitString.indexOf('\\') == -1); + const unitString = parser.GetArgument('unit'); + const isLiteral = (unitString.indexOf('\\') === -1); const unitPieces = parseUnit(parser, unitString, globalOptions, localOptions, isLiteral); - if (globalOptions.parseNumbers) { + if (globalOptions["parse-numbers"]) { // going to assume evaluate expression is processed first, THEN the result is parsed normally - if (globalOptions.evaluateExpression) { + if (globalOptions["evaluate-expression"]) { // TODO Sanitize Evaluate Expression! let expression = globalOptions.expression expression = expression.replace('#1', text); - let result = eval(expression); - text = result.toString(); + text= eval(expression).toString(); } const numlist = parseProductList(parser, text, globalOptions); - if (globalOptions.productExponents === 'individual'){ + if (globalOptions["product-exponents"] === 'individual'){ numlist.forEach(v=>{ - postProcessNumber(v, globalOptions); + postProcessNumber(parser, v, globalOptions); }); } else { const targetExponent = numlist[0].exponentSign + numlist[0].exponent; const altOptions = Object.assign(globalOptions, { exponentMode: 'fixed', fixedExponent: targetExponent }); numlist.forEach((v,i)=>{ - if (i == 0){ - postProcessNumber(v, globalOptions); + if (i === 0){ + postProcessNumber(parser, v, globalOptions); } else { - postProcessNumber(v, altOptions); + postProcessNumber(parser, v, altOptions); } }); } // Need to process this after number because some options alter unit prefixes - if (globalOptions.productUnits === 'power' || globalOptions.productUnits === 'bracket-power'){ + if (globalOptions["product-units"] === 'power' || globalOptions["product-units"] === 'bracket-power'){ const multiplier = numlist.length; unitPieces.forEach(v=>{ if (v.power){ @@ -94,19 +96,20 @@ export function processQuantityProduct(parser: TexParser): void { }) } - let unitDisplay = displayUnits(parser, unitPieces, globalOptions, isLiteral); - const unitNodes = [(new TexParser(unitDisplay, parser.stack.env, parser.configuration)).mml()]; + const unitDisplay = displayUnits(parser, unitPieces, globalOptions, isLiteral); + let unitNode = (new TexParser(unitDisplay, parser.stack.env, parser.configuration)).mml(); const quantityProductNode = createQuantityProductMml(parser, globalOptions); if (quantityProductNode){ - unitNodes.splice(0,0,quantityProductNode); + const root = parser.create('node', 'inferredMrow', [], {}); + root.appendChild(quantityProductNode); + root.appendChild(unitNode); + unitNode = root; } - + const mapItem = listNumberMap.get(numlist.length) ?? listNumberMap.get(3); - const mmlNodes = mapItem(numlist, unitNodes, parser, globalOptions); - mmlNodes.forEach(v=>{ - parser.Push(v); - }); + const mmlNode = mapItem(numlist, unitNode, parser, globalOptions); + parser.Push(mmlNode); } else { const mml = (new TexParser(text, parser.stack.env, parser.configuration)).mml(); diff --git a/src/qtyrangeMethods.ts b/src/qtyrangeMethods.ts index ff253dd..6c566e4 100644 --- a/src/qtyrangeMethods.ts +++ b/src/qtyrangeMethods.ts @@ -2,67 +2,69 @@ import TexParser from "mathjax-full/js/input/tex/TexParser"; import { IOptions, findOptions } from "./options/options"; import { parseNumber } from "./numMethods"; import { postProcessNumber } from "./numPostProcessMethods"; -import { displayOutputMml } from "./numDisplayMethods"; import { exponentListModeMap } from "./numlistMethods"; import { displayUnits, parseUnit } from "./unitMethods"; import { createQuantityProductMml } from "./qtyMethods"; import { unitListModeMap } from "./qtylistMethods"; export function processQuantityRange(parser: TexParser): void { - const globalOptions: IOptions = { ...parser.options as IOptions }; + const globalOptions: IOptions = { ...parser.options.siunitx as IOptions }; - const localOptions = findOptions(parser); + const localOptions = findOptions(parser, globalOptions); Object.assign(globalOptions, localOptions); - let first = parser.GetArgument('firstNum'); - let last = parser.GetArgument('lastNum'); + const first = parser.GetArgument('firstNum'); + const last = parser.GetArgument('lastNum'); - let unitString = parser.GetArgument('unit'); - const isLiteral = (unitString.indexOf('\\') == -1); + const unitString = parser.GetArgument('unit'); + const isLiteral = (unitString.indexOf('\\') === -1); const unitPieces = parseUnit(parser, unitString, globalOptions, localOptions, isLiteral); - if (globalOptions.parseNumbers) { + if (globalOptions["parse-numbers"]) { const firstNum = parseNumber(parser, first, globalOptions); const lastNum = parseNumber(parser, last, globalOptions); - if (globalOptions.rangeExponents === 'individual'){ - postProcessNumber(firstNum, globalOptions); - postProcessNumber(lastNum, globalOptions); + if (globalOptions["range-exponents"] === 'individual'){ + postProcessNumber(parser, firstNum, globalOptions); + postProcessNumber(parser, lastNum, globalOptions); } else { const targetExponent = firstNum.exponentSign + firstNum.exponent; const altOptions = Object.assign(globalOptions, { exponentMode: 'fixed', fixedExponent: targetExponent }); - postProcessNumber(firstNum, globalOptions); - postProcessNumber(lastNum, altOptions); + postProcessNumber(parser, firstNum, globalOptions); + postProcessNumber(parser, lastNum, altOptions); } // Need to process this after number because some options alter unit prefixes - let unitDisplay = displayUnits(parser, unitPieces, globalOptions, isLiteral); - const unitNodes = [(new TexParser(unitDisplay, parser.stack.env, parser.configuration)).mml()]; + const unitDisplay = displayUnits(parser, unitPieces, globalOptions, isLiteral); + let unitNode = (new TexParser(unitDisplay, parser.stack.env, parser.configuration)).mml(); const quantityProductNode = createQuantityProductMml(parser, globalOptions); if (quantityProductNode){ - unitNodes.splice(0,0,quantityProductNode); + const root = parser.create('node', 'inferredMrow', [], {}); + root.appendChild(quantityProductNode); + root.appendChild(unitNode); + unitNode = root; } - const exponentMapItem = exponentListModeMap.get(globalOptions.rangeExponents); + const exponentMapItem = exponentListModeMap.get(globalOptions["range-exponents"]); const exponentResult = exponentMapItem([firstNum, lastNum], parser, globalOptions); - const unitsMapItem = unitListModeMap.get(globalOptions.rangeUnits); - const unitsResult = unitsMapItem(exponentResult, unitNodes, parser,globalOptions); + const unitsMapItem = unitListModeMap.get(globalOptions["range-units"]); + const unitsResult = unitsMapItem(exponentResult, unitNode, parser,globalOptions); - const separator = (new TexParser(`\\text{${globalOptions.rangePhrase}}`, parser.stack.env, parser.configuration)).mml(); - - let total = []; + const separator = (new TexParser(`\\text{${globalOptions["range-phrase"]}}`, parser.stack.env, parser.configuration)).mml(); + + const root = parser.create('node', 'inferredMrow', [], {}); if (exponentResult.leading){ - total.push(exponentResult.leading); + root.appendChild(exponentResult.leading); } - total = total.concat(unitsResult.numbers[0]).concat(separator).concat(unitsResult.numbers[1]); + root.appendChild(unitsResult.numbers[0]); + root.appendChild(separator); + root.appendChild(unitsResult.numbers[1]); if (exponentResult.trailing){ - total = total.concat(exponentResult.trailing); + root.appendChild(exponentResult.trailing); } - total.forEach(v=>{ - parser.Push(v); - }); + parser.Push(root); } else { const mml = (new TexParser(first + last, parser.stack.env, parser.configuration)).mml(); diff --git a/src/siunitx.ts b/src/siunitx.ts index 27295a1..e6ddef4 100644 --- a/src/siunitx.ts +++ b/src/siunitx.ts @@ -1,17 +1,16 @@ import { TeX } from 'mathjax-full/js/input/tex'; import { Configuration, ParserConfiguration } from 'mathjax-full/js/input/tex/Configuration'; -import { CommandMap, CharacterMap, parseResult } from 'mathjax-full/js/input/tex/SymbolMap'; +import { CommandMap, CharacterMap } from 'mathjax-full/js/input/tex/SymbolMap'; import TexParser from 'mathjax-full/js/input/tex/TexParser'; import { processAngle } from './angMethods'; import { processNumber } from './numMethods'; import { findOptions, IOptions, processSISetup, siunitxDefaults, } from './options/options'; import { processQuantity } from './qtyMethods'; import { processUnit } from './unitMethods'; -import { userDefinedUnitOptions, userDefinedUnits } from './units'; import { GetArgumentMML } from "./aria-label"; import NodeUtil from 'mathjax-full/js/input/tex/NodeUtil'; -import { Symbol } from 'mathjax-full/js/input/tex/Symbol' +import { Symbol as TexSymbol } from 'mathjax-full/js/input/tex/Symbol' import { TexConstant } from 'mathjax-full/js/input/tex/TexConstants'; import { processComplexNumber, processComplexQuantity } from './complexMethods'; import { processNumberList } from './numlistMethods'; @@ -20,21 +19,17 @@ import { processNumberRange } from './numrangeMethods'; import { processQuantityList } from './qtylistMethods'; import { processQuantityRange } from './qtyrangeMethods'; import { processQuantityProduct } from './qtyproductMethods'; +import './options/patch'; const methodMap: Record void> = { '\\num': (parser: TexParser): void => { - const nodes = processNumber(parser); - nodes.forEach(v => { - parser.Push(v); - }) + parser.Push(processNumber(parser)); }, '\\ang': (parser: TexParser): void => { - const node = processAngle(parser); - parser.Push(node); + parser.Push(processAngle(parser)); }, '\\unit': (parser: TexParser): void => { - const node = processUnit(parser); - parser.Push(node); + parser.Push(processUnit(parser)); }, '\\qty': (parser: TexParser): void => { processQuantity(parser); // doesn't return a node, pushes internally @@ -58,57 +53,53 @@ const methodMap: Record void> = { processQuantityRange(parser); }, '\\complexnum': (parser: TexParser): void => { - const nodes = processComplexNumber(parser); - nodes.forEach(v => { - parser.Push(v); - }) + parser.Push(processComplexNumber(parser)); }, '\\complexqty': (parser: TexParser): void => { processComplexQuantity(parser); }, - '@{}S': (parser: TexParser): void => { + + '@{}S': (_parser: TexParser): void => { //TODO: NOT IMPLEMENTED // no tabular in MathJax, but maybe use \\begin{array} ? or pure html somehow }, - '\\tablenum': (parser: TexParser): void => { + '\\tablenum': (_parser: TexParser): void => { //TODO: NOT IMPLEMENTED }, '\\sisetup': (parser: TexParser): void => { processSISetup(parser); - } -}; + }, + '\\DeclareSIUnit': (parser: TexParser): void => { + const packageData = parser.configuration.packageData.get('siunitx'); + const userDefinedUnits = packageData[UserDefinedUnitsKey] as Map; + const userDefinedUnitOptions = packageData[UserDefinedUnitOptionsKey] as Map>; -const declareMap: Record) => void> = { - '\\DeclareSIUnit': (parser: TexParser, name: string, options: Partial): void => { - const userDefinedUnits = parser.configuration.packageData.get(UserDefinedUnitsKey) as Map; - const userDefinedUnitOptions = parser.configuration.packageData.get(UserDefinedUnitOptionsKey) as Map>; + const options = findOptions(parser, siunitxDefaults); - const newUnitMacro = parser.GetArgument(name); - const newSymbol = parser.GetArgument(name); + const newUnitMacro = parser.GetArgument('DeclareSIUnit'); + const newSymbol = parser.GetArgument('DeclareSIUnit'); userDefinedUnits.set(newUnitMacro, newSymbol); if (options !== undefined) { userDefinedUnitOptions.set(newUnitMacro, options); } }, - '\\DeclareSIQualifier': (parser: TexParser, name: string, options: Partial): void => { + '\\DeclareSIQualifier': (_parser: TexParser): void => { //TODO: DeclareSIQualifier (eg g_{salt} for "grams of salt") }, - '\\DeclareSIPower': (parser: TexParser, name: string, options: Partial): void => { + '\\DeclareSIPower': (_parser: TexParser): void => { //TODO: DeclareSIPower (eg \square,\cubic,\squared,\cubed) }, }; -export let GlobalParser: TexParser; - export const UserDefinedUnitsKey = 'siunitxUnits'; export const UserDefinedUnitOptionsKey = 'siunitxUnitOptions'; -function angleChars(parser: TexParser, mchar: Symbol) { +function angleChars(parser: TexParser, mchar: TexSymbol) { const def = mchar.attributes || {}; def.mathvariant = TexConstant.Variant.NORMAL; def.class = 'MathML-Unit'; - const emptyToken = parser.create('token', 'mi', { 'data-semantic-type': 'empty' }); + const emptyToken = parser.create('token', 'mi'); const symbolToken = parser.create('token', 'mi', def, mchar.char); const msupNode = parser.create('node', 'msup', [emptyToken, symbolToken]); parser.Push(msupNode); @@ -133,29 +124,23 @@ new CommandMap('siunitxMap', { qtylist: ['siunitxToken', 'qtylist'], qtyrange: ['siunitxToken', 'qtyrange'], qtyproduct: ['siunitxToken', 'qtyproduct'], - DeclareSIUnit: ['siunitxGlobal', 'DeclareSIUnit'], + DeclareSIUnit: ['siunitxToken', 'DeclareSIUnit'], sisetup: ['siunitxToken', 'sisetup'], arialabel: ['Arialabel', 'arialabel'], data: ['Dataset', 'data'], }, { siunitxToken: (parser, name) => { - GlobalParser = parser; methodMap[name as string]?.(parser); }, - siunitxGlobal: (parser, name) => { - GlobalParser = parser; - const options = findOptions(parser); - declareMap[name as string]?.(parser, name as string, options); - }, Arialabel: (parser: TexParser, name: string) => { - let thelabel = parser.GetArgument(name); + const thelabel = parser.GetArgument(name); const arg = GetArgumentMML(parser, name); NodeUtil.setAttribute(arg, 'aria-label', thelabel); parser.Push(arg); }, // currently not used Dataset: (parser: TexParser, name: string) => { - let dataset = parser.GetArgument(name); + const dataset = parser.GetArgument(name); const arg = GetArgumentMML(parser, name); //parse dataset to get both sides of equal const pair = dataset.split('='); @@ -172,8 +157,10 @@ new CommandMap('siunitxMap', { // eslint-disable-next-line @typescript-eslint/no-explicit-any const config = (_config: ParserConfiguration, jax: TeX) => { - jax.parseOptions.packageData.set(UserDefinedUnitsKey, userDefinedUnits); - jax.parseOptions.packageData.set(UserDefinedUnitOptionsKey, userDefinedUnitOptions); + jax.parseOptions.packageData.set('siunitx', { + [UserDefinedUnitsKey]: new Map(), + [UserDefinedUnitOptionsKey]: new Map() + }); }; @@ -182,6 +169,8 @@ Configuration.create('siunitx', handler: { macro: ['angchar-symbols', 'siunitxMap'] }, - options: siunitxDefaults, + options: { + siunitx: siunitxDefaults + }, config: config }); diff --git a/src/unitMethods.ts b/src/unitMethods.ts index c87906a..d5268b2 100644 --- a/src/unitMethods.ts +++ b/src/unitMethods.ts @@ -1,11 +1,11 @@ import { MmlNode } from "mathjax-full/js/core/MmlTree/MmlNode"; import TexError from "mathjax-full/js/input/tex/TexError"; import TexParser from "mathjax-full/js/input/tex/TexParser"; -import { siunitxError } from "./errors"; -import { findOptions, IOptions, processOptions } from "./options/options"; +import { siunitxError } from "./error/errors"; +import { findOptions, IOptions } from "./options/options"; import { IUnitOptions, QualifierMode } from "./options/unitOptions"; import { UserDefinedUnitOptionsKey, UserDefinedUnitsKey } from "./siunitx"; -import { prefixSymbol, unitNameLookup, unitSymbol, unitSymbolsWithShortcuts } from "./units"; +import { prefixSymbol, unitSymbol, unitSymbolsWithShortcuts } from "./units"; export interface IUnitPiece { symbol?: string; @@ -47,10 +47,10 @@ function processUnitMacro(macro: string, parser: TexParser): IUnitMacroProcessRe return { type: 'prefix', result: { prefix: prefixSymbol.get(macro) } }; } - const userDefinedUnits = parser.configuration.packageData.get(UserDefinedUnitsKey) as Map; + const userDefinedUnits = parser.configuration.packageData.get('siunitx')[UserDefinedUnitsKey] as Map; if (userDefinedUnits.has('\\' + macro)) { const result = userDefinedUnits.get('\\' + macro); - const userDefinedUnitOptions = parser.configuration.packageData.get(UserDefinedUnitOptionsKey) as Map>; + const userDefinedUnitOptions = parser.configuration.packageData.get('siunitx')[UserDefinedUnitOptionsKey] as Map>; const options = userDefinedUnitOptions.get('\\' + macro); return { type: 'unit', result: { symbol: result as string, prefix: '' }, options: options }; } @@ -103,26 +103,26 @@ function unitLatex(unitPiece: IUnitPiece, options: IUnitOptions, absPower = fals if (unitPiece.highlight) { unitLatex += `{\\color{${unitPiece.highlight}}`; } - unitLatex += options.unitFontCommand + '{'; + unitLatex += options["unit-font-command"] + '{'; //check for square root - if (options.powerHalfAsSqrt && unitPiece.power && unitPiece.power == 0.5) { + if (options["power-half-as-sqrt"] && unitPiece.power && unitPiece.power === 0.5) { unitLatex += `\\sqrt{\\class{MathML-Unit}{${unitPiece.prefix}${unitPiece.symbol}}}`; unitPiece.power = null; } else { unitLatex += `\\class{MathML-Unit}{${unitPiece.prefix}${unitPiece.symbol}}`; } if (unitPiece.qualifier) { - unitLatex += qualiferMethod.get(options.qualifierMode)?.(unitPiece.qualifier, options.qualifierPhrase); + unitLatex += qualiferMethod.get(options["qualifier-mode"])?.(unitPiece.qualifier, options["qualifier-phrase"]); } unitLatex += '}'; - const power = unitPiece.power != null + const power = (unitPiece.power !== undefined && unitPiece.power !== null) ? (absPower - ? Math.abs(unitPiece.power * (unitPiece.position == 'denominator' ? -1 : 1)) - : unitPiece.power * (unitPiece.position == 'denominator' ? -1 : 1)) + ? Math.abs(unitPiece.power * (unitPiece.position === 'denominator' ? -1 : 1)) + : unitPiece.power * (unitPiece.position === 'denominator' ? -1 : 1)) : (absPower - ? Math.abs(1 * (unitPiece.position == 'denominator' ? -1 : 1)) - : 1 * (unitPiece.position == 'denominator' ? -1 : 1)); - if (power != null && power != 1) { + ? Math.abs(1 * (unitPiece.position === 'denominator' ? -1 : 1)) + : 1 * (unitPiece.position === 'denominator' ? -1 : 1)); + if (power !== null && power !== undefined && power !== 1) { unitLatex += '^{' + power + '}'; } if (unitPiece.cancel) { @@ -131,27 +131,26 @@ function unitLatex(unitPiece: IUnitPiece, options: IUnitOptions, absPower = fals if (unitPiece.highlight) { unitLatex += '}'; } - return { latex: unitLatex, superscriptPresent: power != 1 }; + return { latex: unitLatex, superscriptPresent: power !== 1 }; } export function displayUnits(parser: TexParser, unitPieces: Array, options: IOptions, isLiteral: boolean): string { - //const mainOptions = parser.configuration.packageData.get('siunitx') as IUnitOptions; let closeColor: boolean = false; let texString = ''; - if (options.unitColor != '') { - texString += `{\\color{${options.unitColor}}`; + if (options["unit-color"] !== '') { + texString += `{\\color{${options["unit-color"]}}`; closeColor = true; - } else if (options.color != '') { + } else if (options.color !== '') { texString += `{\\color{${options.color}}`; closeColor = true; } let perForSingle = false; if (unitPieces.length >= 2 && unitPieces.filter((v) => { - const power = v.power != null - ? (v.power * (v.position == 'denominator' ? -1 : 1)) + const power = (v.power !== null && v.power !== undefined) + ? (v.power * (v.position === 'denominator' ? -1 : 1)) : 1; - return Math.sign(power) == -1; - }).length == 1 && options.perMode == 'single-symbol') { + return Math.sign(power) === -1; + }).length === 1 && options["per-mode"] === 'single-symbol') { perForSingle = true; } @@ -168,13 +167,13 @@ export function displayUnits(parser: TexParser, unitPieces: Array, o } }); unitPieces.forEach((v) => { - if (v == startsSlash) { + if (v === startsSlash) { latex += ' / '; } const latexResult = unitLatex(v, options); // eslint-disable-next-line @typescript-eslint/no-unused-vars - if (latex != '') { - latex += options.interUnitProduct; + if (latex !== '') { + latex += options["inter-unit-product"]; } latex += latexResult.latex; @@ -187,59 +186,59 @@ export function displayUnits(parser: TexParser, unitPieces: Array, o let numeratorCount = 0; let denominatorCount = 0; unitPieces.forEach((v) => { - if (v.position === 'denominator' || (v.power != null && v.power < 0)) { + if (v.position === 'denominator' || (v.power !== null && v.power !== undefined && v.power < 0)) { denominatorCount++; } else { numeratorCount++; } }, 0); - if (options.perMode == 'fraction' || options.perMode == 'symbol' - || options.perMode == 'repeated-symbol' || perForSingle || (options.perMode === 'single-symbol' && denominatorCount == 1 && numeratorCount > 0)) { + if (options["per-mode"] === 'fraction' || options["per-mode"] === 'symbol' + || options["per-mode"] === 'repeated-symbol' || perForSingle || (options["per-mode"] === 'single-symbol' && denominatorCount === 1 && numeratorCount > 0)) { let numerator = ''; let denominator = ''; let lastNumeratorHadSuperscript = false; unitPieces.forEach((v) => { let latexResult; - if (v.position === 'denominator' || (v.power != null && v.power < 0)) { - latexResult = unitLatex(v, options, options.perMode == 'fraction' || options.perMode == 'symbol' || options.perMode == 'repeated-symbol' || options.perMode == "single-symbol" || perForSingle); + if (v.position === 'denominator' || (v.power !== null && v.power !== undefined && v.power < 0)) { + latexResult = unitLatex(v, options, options["per-mode"] === 'fraction' || options["per-mode"] === 'symbol' || options["per-mode"] === 'repeated-symbol' || options["per-mode"] === "single-symbol" || perForSingle); - if (denominator != '') { - if (options.perMode == 'repeated-symbol') { + if (denominator !== '') { + if (options["per-mode"] === 'repeated-symbol') { if (latexResult.superscriptPresent) { - denominator += options.perSymbolScriptCorrection; + denominator += options["per-symbol-script-correction"]; } - denominator += options.perSymbol; + denominator += options["per-symbol"]; } else { - denominator += options.interUnitProduct; + denominator += options["inter-unit-product"]; } } denominator += latexResult.latex; } else { - latexResult = unitLatex(v, options, options.perMode == 'fraction' || options.perMode == 'symbol' || options.perMode == 'repeated-symbol' || options.perMode == "single-symbol" || perForSingle); + latexResult = unitLatex(v, options, options["per-mode"] === 'fraction' || options["per-mode"] === 'symbol' || options["per-mode"] === 'repeated-symbol' || options["per-mode"] === "single-symbol" || perForSingle); lastNumeratorHadSuperscript = latexResult.superscriptPresent; - if (numerator != '') { - numerator += options.interUnitProduct; + if (numerator !== '') { + numerator += options["inter-unit-product"]; } numerator += latexResult.latex; } }); // if no numerator, use 1... but use nothing if denominator is empty, too - if (numerator == '' && denominator != '') { + if (numerator === '' && denominator !== '') { numerator = '1'; } // if no denominator, then no fraction needed. - if (denominator != '') { + if (denominator !== '') { //adjust denominator if brackets are needed - if (denominatorCount > 1 && options.perMode === 'symbol' && options.bracketUnitDenominator) { + if (denominatorCount > 1 && options["per-mode"] === 'symbol' && options["bracket-unit-denominator"]) { denominator = '(' + denominator + ')'; } - if (options.perMode === 'fraction') { - texString += options.fractionCommand + '{' + numerator + '}{' + denominator + '}'; + if (options["per-mode"] === 'fraction') { + texString += options["fraction-command"] + '{' + numerator + '}{' + denominator + '}'; } - else if (options.perMode === 'repeated-symbol' || options.perMode === 'symbol' || perForSingle || options.perMode === 'single-symbol') { - texString += numerator + (lastNumeratorHadSuperscript ? options.perSymbolScriptCorrection : '') + options.perSymbol + denominator; + else if (options["per-mode"] === 'repeated-symbol' || options["per-mode"] === 'symbol' || perForSingle || options["per-mode"] === 'single-symbol') { + texString += numerator + (lastNumeratorHadSuperscript ? options["per-symbol-script-correction"] : '') + options["per-symbol"] + denominator; } else { console.log("shouldn't be here"); @@ -249,14 +248,14 @@ export function displayUnits(parser: TexParser, unitPieces: Array, o } } else { - if (options.perMode == 'power-positive-first') { + if (options["per-mode"] === 'power-positive-first') { unitPieces = unitPieces.sort((x, y) => { - let a = x.power != null ? x : 1; - if (x.position == 'denominator') { + let a = (x.power !== null && x.power !== undefined) ? x : 1; + if (x.position === 'denominator') { a = -a; } - let b = y.power != null ? y : 1; - if (y.position == 'denominator') { + let b = (y.power !== null && y.power !== undefined) ? y : 1; + if (y.position === 'denominator') { b = -b; } if (a < b) return 1; @@ -265,13 +264,12 @@ export function displayUnits(parser: TexParser, unitPieces: Array, o }); } let latex = ''; - let lastHadSuperscript = false; + //let lastHadSuperscript = false; unitPieces.forEach((v) => { const latexResult = unitLatex(v, options); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - lastHadSuperscript = latexResult.superscriptPresent; - if (latex != '') { - latex += options.interUnitProduct; + //lastHadSuperscript = latexResult.superscriptPresent; + if (latex !== '') { + latex += options["inter-unit-product"]; } latex += latexResult.latex; @@ -308,18 +306,12 @@ export function parseUnit(parser: TexParser, text: string, globalOptions: IOptio Object.assign(globalOptions, processedMacro.options); } // apply immediate options here - //processOptions(globalOptions, localOptionString); - //const options = processOptions(globalOptions, localOptionString); - //const options = {...globalOptions, ...localOptions}; - //console.log(options); Object.assign(globalOptions, localOptions); - //localOptions.forEach((v, k) => globalOptions[k] = v); - //console.log(globalOptions.perMode); switch (processedMacro.type) { case 'next': case 'prefix': - if (nextModifier != null) { + if (nextModifier !== null) { nextModifier = Object.assign(nextModifier, processedMacro.result); } else { nextModifier = processedMacro.result; @@ -327,7 +319,7 @@ export function parseUnit(parser: TexParser, text: string, globalOptions: IOptio break; case 'previous': { - if (unitPieces.length == 0) { + if (unitPieces.length === 0) { throw new TexError("MissingPreviousMacro", "There is no previous macro for %1 to modify.", macro); } let last = unitPieces[unitPieces.length - 1]; @@ -336,12 +328,12 @@ export function parseUnit(parser: TexParser, text: string, globalOptions: IOptio } case 'unit': { - if (nextModifier != null) { + if (nextModifier !== null) { processedMacro.result = Object.assign(processedMacro.result, nextModifier); // TODO: WHY IS THIS parser.options and not globaloptions??? // Is this even needed? repeated-symbol is a display option, not a parsing option. - if ((parser.options as IOptions).perMode === 'repeated-symbol' || globalOptions.stickyPer) { - const denom = nextModifier.position == 'denominator'; + if ((parser.options.siunitx as IOptions)["per-mode"]=== 'repeated-symbol' || globalOptions["sticky-per"]) { + const denom = nextModifier.position === 'denominator'; nextModifier = null; if (denom) { nextModifier = { position: 'denominator' }; @@ -363,13 +355,13 @@ export function parseUnit(parser: TexParser, text: string, globalOptions: IOptio } export function processUnit(parser: TexParser): MmlNode { - const globalOptions: IOptions = { ...parser.options as IOptions }; + const globalOptions: IOptions = { ...parser.options.siunitx as IOptions }; // TODO: may be better done a different way. double check. - const localOptions = findOptions(parser); + const localOptions = findOptions(parser, globalOptions); - if ((localOptions.parseUnits === undefined || localOptions.parseUnits === true) && - globalOptions.parseUnits === true + if ((localOptions["parse-units"] === undefined || localOptions["parse-units"] === true) && + globalOptions["parse-units"] === true ) { const text = parser.GetArgument('unit'); @@ -378,9 +370,9 @@ export function processUnit(parser: TexParser): MmlNode { // If literal, we do NOT apply per-mode settings. // We'll check if text had backslashes and pass that result to the next functions. - const isLiteral = (text.indexOf('\\') == -1); + const isLiteral = (text.indexOf('\\') === -1); // This will only be a global option. - if (globalOptions.forbidLiteralUnits) { + if (globalOptions["forbid-literal-units"]) { throw siunitxError.LiteralUnitsForbidden(text); } @@ -408,11 +400,11 @@ function processPrefixUnitCombo(text: string, unitPiece: IUnitPiece): void { const regex = new RegExp('(' + prefixes + ')?(' + units + ')'); const result = regex.exec(text); - if (result == null) { + if (result === null) { return; } - if (result[1] !== undefined) { + if (result[1] !== undefined && result[1] !== null) { unitPiece.prefix = result[1]; } else { unitPiece.prefix = ''; @@ -421,7 +413,6 @@ function processPrefixUnitCombo(text: string, unitPiece: IUnitPiece): void { } function parsePlainTextUnits(parser: TexParser, text: string): Array { - //const mainOptions = parser.configuration.packageData.get('siunitx') as IUnitOptions; const unitPieces: Array = new Array(); const subParser = new TexParser(text, parser.stack.env, parser.configuration); subParser.i = 0; @@ -452,8 +443,8 @@ function parsePlainTextUnits(parser: TexParser, text: string): Array //power let next = subParser.string.charAt(++subParser.i); let power = ''; - if (next == '{') { - while ((next = subParser.string.charAt(++subParser.i)) != '}') { + if (next === '{') { + while ((next = subParser.string.charAt(++subParser.i)) !== '}') { power += next; } } else { @@ -467,8 +458,8 @@ function parsePlainTextUnits(parser: TexParser, text: string): Array //of let next = subParser.string.charAt(++subParser.i); let qualifier = ''; - if (next == '{') { - while ((next = subParser.string.charAt(++subParser.i)) != '}') { + if (next === '{') { + while ((next = subParser.string.charAt(++subParser.i)) !== '}') { qualifier += next; } } else { diff --git a/src/units.ts b/src/units.ts index 4ae9679..084fe47 100644 --- a/src/units.ts +++ b/src/units.ts @@ -1,8 +1,5 @@ import { TwoWayMap } from "./twoWayMap"; -export const userDefinedUnits = new Map(); -export const userDefinedUnitOptions = new Map(); - export const prefixSymbol = new Map([ ['yotta','Y'], ['zetta','Z'], diff --git a/test.html b/test.html index 0966545..927d421 100644 --- a/test.html +++ b/test.html @@ -20,15 +20,36 @@ - $$ \numlist{0.1;0.2;0.3} $$ - $$ \numlist[list-separator = {; }]{0.1;0.2;0.3} $$ - $$\numlist[list-final-separator = {, }]{0.1;0.2;0.3} $$ - $$ \numlist[ - list-separator = { and }, - list-final-separator = { and finally } - ]{0.1;0.2;0.3} $$ - $$ \numlist{0.1;0.2} $$ - $$ \numlist[list-pair-separator = {, and }]{0.1;0.2} $$ + $$ \qty{2.67}{\farad} $$ +$$ \qty[quantity-product = {\ }]{2.67}{\farad} $$ +$$ \qty[quantity-product = ]{2.67}{\farad} $$ + + $$ \displaylines{ + \ang{-1;;} \\ + \ang{;-2;} \\ + \ang{;;-3} \\ + } $$ + $$ \displaylines{ + \sisetup{fill-angle-degrees} + \ang{-1;;} \\ + \ang{;-2;} \\ + \ang{;;-3} \\ + \sisetup{fill-angle-degrees = false} + } $$ + $$ \displaylines{ + \sisetup{fill-angle-minutes} + \ang{-1;;} \\ + \ang{;-2;} \\ + \ang{;;-3} \\ + \sisetup{fill-angle-minutes = false} + } $$ + $$ \displaylines{ + \sisetup{fill-angle-seconds} + \ang{-1;;} \\ + \ang{;-2;} \\ + \ang{;;-3} \\ + \sisetup{fill-angle-seconds = false} + } $$ \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 9934ce6..a63af12 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,21 +1,30 @@ { "compilerOptions": { - "outDir": "./js", - "noImplicitAny": false, - "module": "ES6", - "target": "ES2015", - "jsx":"preserve", - "allowJs": true, - "sourceMap": true, - "moduleResolution": "node", - "lib":["ES5", "DOM"] + "outDir": "./js", + "noImplicitAny": false, + "module": "ES6", + "target": "ES2015", + "jsx": "preserve", + "allowJs": true, + "sourceMap": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "lib": [ + "ES2015", + "DOM" + ], + "typeRoots": [ + "./node_modules/@types", + "./types" + ] }, "include": [ - "src/**/*" - ], + "src/**/*", + "types/localizationResource.d.ts" +, "types/patch.d.ts" ], "exclude": [ "node_modules", "**/*.spec.ts", "js/**/*" ] - } \ No newline at end of file +} \ No newline at end of file diff --git a/types/localizationResource.d.ts b/types/localizationResource.d.ts new file mode 100644 index 0000000..9a634b1 --- /dev/null +++ b/types/localizationResource.d.ts @@ -0,0 +1,18 @@ +interface LocalizationResource { + badOptionChars: string; + comparatorAlreadySet: string; + exponentThresholdsError: string; + extraSemicolon: string; + invalidAngArgument: string; + invalidNumArgument: string; + invalidOptionValue: string; + literalUnitsForbidden: string; + macroNotDefined: string; + noUncertaintyToClose: string; + uncertaintyAlreadyClosed: string; +} + +declare module "src/error/resource.*.json" { + const val: LocalizationResource; + export = val; +} \ No newline at end of file