diff --git a/README.md b/README.md index 00b9acceac..145644ef38 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,52 @@ -![screenshot](https://ccd0.github.io/4chan-x/img/screenshot.png) +# 4chan XT + +**This repo is work in progress!** Use the build from the [repo this is forked from](https://github.com/ccd0/4chan-x) in the meantime. + +The 4chan XT project is a migration of 4chan X from coffeescript to TypeScript/JavaScript. It is named XT both as a continuation of eXTended, and a T for TypeScript. The goals of this project is to first get a working bundle from js/ts files, and then gradually convert js files to ts and add types as needed. + +## TODO + +- find alternative for `<% if (` + - [x] made html templates jsx/txt functions + - this uses the typescript compiler to compile the jsx + - render code is in [src/globals/jsx.ts](./src/globals/jsx.ts) + - [x] binary files are included as base64 in the bundle step, they do need explicit imports + - [ ] \<% if (readJSON('/.tests_enabled')) { %\>, are these still used? +- build script + - [x] userscript + - [ ] .crx extension + - [x] crx directory that can be loaded as an unpacked extension is created + - [x] beta + - [x] noupdate +- [ ] run and debug +- [ ] port updates made to 4chan-X made since this was forked + +## Other notes + +- A lot of files have circular dependencies, but rollup can handle that + - but for some scripts that add to the same object I had to merge them, like Posting/QR and site/SW.yotsuba.js + - sometimes something might not be initialized before use, for example, `$.dict()` and `$.SECONDS` + - I moved these to a new file called helpers.ts, which shouldn't have dependencies itself, so it's also available +- tsconfig.json has `"checkJs": true,`, and a lot of js files report type errors when opened because of unknown properties on objects and reassigning variables with different types. These errors don't block the bundle at this moment. +- old files in the builds directory stay as reference until the new builds are functional, new files go in the builds/test directory +- old build scripts are also kept around for reference until the new build output is fully functional +- the es 2020 target was choses for optional chaining +- @violentmonkey/types was chosen over @types/greasemonkey because @types/greasemonkey only declares the GM object, and not GM\_ functions +- I don't really understand PostClone, so that might have bugs after migration + +## Recommendations + +- Visual studio code +- Format on save using default formatter +- extensions + - streetsidesoftware.code-spell-checker + - EditorConfig.EditorConfig + +--- + +Original readme: + +![screenshot](./img/screenshot.png) # 4chan X 4chan X is a script that adds various features to anonymous imageboards. It was originally developed for 4chan but has no affiliation with it. @@ -90,4 +138,3 @@ If you encounter a bug, try the steps [here](https://github.com/ccd0/4chan-x/blo - [Frequently Asked Questions](https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions) - [Report Bugs](https://github.com/ccd0/4chan-x/issues) - [Contributing](https://github.com/ccd0/4chan-x/blob/master/CONTRIBUTING.md) - diff --git a/decaffinate.md b/decaffinate.md deleted file mode 100644 index 23938e2810..0000000000 --- a/decaffinate.md +++ /dev/null @@ -1,6 +0,0 @@ -# migration from coffeescript to modern js/ts - -## TODO - -- look for TODO comments -- find alternative for `<% if (` diff --git a/package-lock.json b/package-lock.json index ac487c011d..e416676920 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,18 @@ { - "name": "4chan-X", - "lockfileVersion": 2, + "name": "4chan-XT", + "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "4chan-X", + "name": "4chan-XT", "license": "MIT", "devDependencies": { + "@rollup/plugin-typescript": "^11.0.0", "@rollup/pluginutils": "^5.0.2", + "@types/chrome": "^0.0.217", + "@types/node": "^18.14.5", + "@violentmonkey/types": "^0.1.5", "chrome-webstore-upload": "^0.4.4", - "coffeescript": "=1.12.7", - "decaffeinate": "^8.1.3", "esprima": "^4.0.1", "font-awesome": "=4.7.0", "jshint": "^2.13.4", @@ -19,2913 +21,272 @@ "markdown-it": "^12.3.2", "markdown-it-anchor": "^7.1.0", "request": "^2.88.2", - "rollup": "^3.17.2" + "rollup": "^3.17.2", + "tslib": "^2.5.0", + "typescript": "^4.9.5" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "node_modules/@rollup/plugin-typescript": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.0.0.tgz", + "integrity": "sha512-goPyCWBiimk1iJgSTgsehFD5OOFHiAknrRJjqFCudcW8JtWiBlK284Xnn4flqMqg6YAjVG/EE+3aVzrL5qNSzQ==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@rollup/pluginutils": "^5.0.1", + "resolve": "^1.22.1" }, "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", - "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.0", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.0", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.0", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", - "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.21.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", - "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", - "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.1", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.2", - "@babel/types": "^7.21.2", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", - "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@codemod/core": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@codemod/core/-/core-2.2.0.tgz", - "integrity": "sha512-H2Qa+hbHFf05xl4YpmvCoczgozQohl+cNiLlCDOmnwcSAgnurZCMRbAR7ppkXjoZ9LRosw3OyY9MTpiKhngWIg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.20.12", - "@babel/generator": "^7.20.14", - "@codemod/parser": "^1.4.0", - "is-ci-cli": "^2.2.0", - "recast": "^0.19.0", - "resolve": "^1.12.0" - } - }, - "node_modules/@codemod/parser": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@codemod/parser/-/parser-1.4.0.tgz", - "integrity": "sha512-Qi7PVgvA+RzM8Zzx0BnhH4CnWuwvD4A3/QJZGSJLELxJZwQVI3dT0o/7WLD9lLAkdz0n2Rep/REpfVdTNap/dg==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.15" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "node_modules/@resugar/codemod-declarations-block-scope": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@resugar/codemod-declarations-block-scope/-/codemod-declarations-block-scope-1.0.3.tgz", - "integrity": "sha512-jnoNiz/PY4zZXPaLIeiKkGX6xau83Vn2lJRffIKuTPRMRTegFD2G7MmjxgZx9htJ/H9huqKJIhhqfj4VbuaDbA==", - "dev": true - }, - "node_modules/@resugar/codemod-functions-arrow": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@resugar/codemod-functions-arrow/-/codemod-functions-arrow-1.0.2.tgz", - "integrity": "sha512-6dfHOEIxZw6sc0auiDpELDRYGFfVhrJwc6xN4JxpGE9vc3Hg954svu8MhedSV7VTVSClLv4CE0RvUOD+09DAuw==", - "dev": true, - "dependencies": { - "@resugar/helper-comments": "^1.0.0" - } - }, - "node_modules/@resugar/codemod-modules-commonjs": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@resugar/codemod-modules-commonjs/-/codemod-modules-commonjs-1.0.5.tgz", - "integrity": "sha512-A1lBMZWNEUFpmSGgTRzEaRUOWxZ/aNjil2ekCfjiITRJ3UrvsSF7PIPgv5waKzsgimtiQ0Wm+fcqFko0itZ21A==", - "dev": true, - "dependencies": { - "@resugar/helper-comments": "^1.0.0" - } - }, - "node_modules/@resugar/codemod-objects-concise": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@resugar/codemod-objects-concise/-/codemod-objects-concise-1.0.2.tgz", - "integrity": "sha512-Qf/zKkBzfGg1Q+2HSrssPllKYrwLxuDF/p39pz10keJtAhkYEcCbWcaHJRr5TgV9Pl9qnzzMImP9TG6KBwx7eA==", - "dev": true - }, - "node_modules/@resugar/codemod-objects-destructuring": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@resugar/codemod-objects-destructuring/-/codemod-objects-destructuring-1.0.1.tgz", - "integrity": "sha512-xvJ1YAsV3/eSgL7BaSXRNnnz3B8f+dv+KNpi/07RF46L5ah3E8TLb2Ge/IgJB9lz/mhTDfB+pmFem9J3ndfN6w==", - "dev": true - }, - "node_modules/@resugar/codemod-objects-shorthand": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@resugar/codemod-objects-shorthand/-/codemod-objects-shorthand-1.0.1.tgz", - "integrity": "sha512-1QZxpc2cX1DgxAWPDrp4l9VD1d+RZrqOXcXuckQmEIHr5TeSOhdBs2r3zFEbijxCTKD7fWsFgkas7LKh+KqGvQ==", - "dev": true, - "dependencies": { - "@resugar/helper-comments": "^1.0.0" - } - }, - "node_modules/@resugar/codemod-strings-template": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@resugar/codemod-strings-template/-/codemod-strings-template-1.0.1.tgz", - "integrity": "sha512-ruVwOE67lmFGiyVr8I60l5lEDi0xKk1FYXyTGKMZdiiM2+W+L/qquOEqZmxOPA/xDNc5JG9VZXQ+4TZH9HqYxw==", - "dev": true - }, - "node_modules/@resugar/helper-comments": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@resugar/helper-comments/-/helper-comments-1.0.2.tgz", - "integrity": "sha512-AXXKDyRcTN3naXhfvu/2mWX5OTc4RqlJIERCfnFs+M2VrawqoZU+oFS3L4ZsdP+/4rcofMCs9CMsYKc4S40PPQ==", - "dev": true - }, - "node_modules/@rollup/pluginutils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", - "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@sindresorhus/is": { - "version": "3.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@types/cacheable-request": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", - "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", - "dev": true - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/keyv": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "14.14.20", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/responselike": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/add-variable-declarations": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/add-variable-declarations/-/add-variable-declarations-4.0.7.tgz", - "integrity": "sha512-SMjwvYGKrpk8CIN5K4LYpR54k9XpNmUdZLXOCvQvSvYnYTsvprouAuktct+ysSn1v2YVjQy4Du6JxwGhRV7Y1g==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.6.2", - "@babel/types": "^7.6.1", - "@codemod/parser": "^1.0.3", - "magic-string": "^0.25.3" - } - }, - "node_modules/add-variable-declarations/node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/asn1": { - "version": "0.2.4", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/ast-types": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.3.tgz", - "integrity": "sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/automatic-semicolon-insertion": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/automatic-semicolon-insertion/-/automatic-semicolon-insertion-3.0.6.tgz", - "integrity": "sha512-M/+n8XFfrGzjgjXm+zzaCEy9nqiM+YkxQUN/P8Kfdu2i9FPvix7ece4B9kBN8Ca76UYiBJRQ+5u9DlLa7ownsA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.4" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.11.0", - "dev": true, - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001457", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", - "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] - }, - "node_modules/caseless": { - "version": "0.12.0", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chrome-webstore-upload": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/chrome-webstore-upload/-/chrome-webstore-upload-0.4.4.tgz", - "integrity": "sha512-2afVQYCVfLAwYPPuCqKLivDl/CZcaRbmxHOq8B6oG1CbOPWncKl7yJR0G9LOu2wPfRprIEv3QGVQw3qRxuiBEw==", - "dev": true, - "dependencies": { - "got": "11.5.2" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "node_modules/cli": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "exit": "0.1.2", - "glob": "^7.1.1" - }, - "engines": { - "node": ">=0.2.5" - } - }, - "node_modules/clone-response": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^1.0.0" - } - }, - "node_modules/coffee-lex": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/coffee-lex/-/coffee-lex-9.3.2.tgz", - "integrity": "sha512-lft4PwdLuqh3q50uR+yU5rpBKif1asJsXFPQ8AHDLs9woru6ENJ7jUVLGHk8cuCktvPFxaIzNeES9bNuSd89RA==", - "dev": true - }, - "node_modules/coffeescript": { - "version": "1.12.7", - "dev": true, - "license": "MIT", - "bin": { - "cake": "bin/cake", - "coffee": "bin/coffee" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/console-browserify": { - "version": "1.1.0", - "dev": true, - "dependencies": { - "date-now": "^0.1.4" - } - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/dashdash": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/date-now": { - "version": "0.1.4", - "dev": true - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decaffeinate": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/decaffeinate/-/decaffeinate-8.1.3.tgz", - "integrity": "sha512-5IOmQ5Ym7tlurUeSJWDwCncbdl9w5SE+XnZXzONY5ZoVDjetlnJIs3cvOKzJNbtSJJo8GNteH3jBa274mkDhpw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.12.6", - "@codemod/core": "^2.0.1", - "@codemod/parser": "^1.2.1", - "@resugar/codemod-declarations-block-scope": "^1.0.3", - "@resugar/codemod-functions-arrow": "^1.0.2", - "@resugar/codemod-modules-commonjs": "^1.0.5", - "@resugar/codemod-objects-concise": "^1.0.2", - "@resugar/codemod-objects-destructuring": "^1.0.1", - "@resugar/codemod-objects-shorthand": "^1.0.1", - "@resugar/codemod-strings-template": "^1.0.1", - "add-variable-declarations": "^4.0.7", - "automatic-semicolon-insertion": "^3.0.2", - "coffee-lex": "^9.3.2", - "decaffeinate-coffeescript": "1.12.7-patch.4", - "decaffeinate-coffeescript2": "2.2.1-patch.6", - "decaffeinate-parser": "^23.0.1", - "detect-indent": "^4.0.0", - "is-ci-cli": "^2.2.0", - "lines-and-columns": "^2.0.3", - "magic-string": "^0.26.2", - "mz": "^2.7.0", - "tslib": "^2.4.0" - }, - "bin": { - "decaffeinate": "bin/decaffeinate" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/decaffeinate-coffeescript": { - "version": "1.12.7-patch.4", - "resolved": "https://registry.npmjs.org/decaffeinate-coffeescript/-/decaffeinate-coffeescript-1.12.7-patch.4.tgz", - "integrity": "sha512-VLVyuAahCJMskqpbgcyg7MrxIVeooPDWoU8atvotH+OgLdIIJlKzFFHZoJCTaMdSxqtbCkli7dFzCeqnTixp5Q==", - "dev": true, - "bin": { - "cake": "bin/cake", - "coffee": "bin/coffee" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/decaffeinate-coffeescript2": { - "version": "2.2.1-patch.6", - "resolved": "https://registry.npmjs.org/decaffeinate-coffeescript2/-/decaffeinate-coffeescript2-2.2.1-patch.6.tgz", - "integrity": "sha512-R9QhWXDmRGs/uSPliv5n3FALNQwHYlkf6lhP5ZZ0QQ8NqZZJNfqLuiiSwe2t2/o6hHmIEkRSfQL/Ex1SEhSAHg==", - "dev": true, - "bin": { - "cake": "bin/cake", - "coffee": "bin/coffee" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/decaffeinate-parser": { - "version": "23.0.1", - "resolved": "https://registry.npmjs.org/decaffeinate-parser/-/decaffeinate-parser-23.0.1.tgz", - "integrity": "sha512-H0fuEdP2GWDhFIg4Ejlx2Loz6/yqEvWR3i2+mU8TNJkkJ9txd/4TEIdLcdMmBd9vw3eeBs0HHH+Yj8QbRO5aZQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.4", - "@codemod/parser": "^1.2.1", - "coffee-lex": "^9.3.1", - "decaffeinate-coffeescript": "^1.12.7-patch.4", - "decaffeinate-coffeescript2": "^2.2.1-patch.6", - "lines-and-columns": "^2.0.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha512-BDKtmHlOzwI7iRuEkhzsnPoi5ypEhWAJB5RvHWe1kMr06js3uK5B3734i3ui5Yd+wOJV1cpE4JnivPD283GU/A==", - "dev": true, - "dependencies": { - "repeating": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dom-serializer": { - "version": "0.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - } - }, - "node_modules/dom-serializer/node_modules/domelementtype": { - "version": "2.1.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "2.1.0", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "1.3.1", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "2.3.0", - "dev": true, - "dependencies": { - "domelementtype": "1" - } - }, - "node_modules/domutils": { - "version": "1.5.1", - "dev": true, - "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.308", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.308.tgz", - "integrity": "sha512-qyTx2aDFjEni4UnRWEME9ubd2Xc9c0zerTUl/ZinvD4QPsF0S7kJTV/Es/lPCTkNX6smyYar+z/n8Cl6pFr8yQ==", - "dev": true - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/entities": { - "version": "1.0.0", - "dev": true, - "license": "BSD-like" - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "node_modules/exit": { - "version": "0.1.2", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/font-awesome": { - "version": "4.7.0", - "dev": true, - "license": "(OFL-1.1 AND MIT)", - "engines": { - "node": ">=0.10.3" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-stream": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/glob": { - "version": "7.1.6", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/got": { - "version": "11.5.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^3.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.1", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.0", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/har-schema": { - "version": "2.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "deprecated": "this library is no longer supported", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/htmlparser2": { - "version": "3.8.3", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "1", - "domhandler": "2.3", - "domutils": "1.5", - "entities": "1.0", - "readable-stream": "1.1" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.1.0", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/http-signature": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/http2-wrapper": { - "version": "1.0.0-beta.5.2", - "dev": true, - "license": "MIT", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "license": "ISC" - }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-ci-cli": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-ci-cli/-/is-ci-cli-2.2.0.tgz", - "integrity": "sha512-Xg97ZGDzU0a9gPTAli+TNegMk+PI3x0KLRYCfBa2LAboF1YyuA03Gwdc9vpu3VRNU+lFFNkvXnIQuJ0PgB120Q==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "is-ci": "^2.0.0" - }, - "bin": { - "is-ci": "cli.js", - "is-ci-cli": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/isarray": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isstream": { - "version": "0.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/jsbn": { - "version": "0.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/jshint": { - "version": "2.13.4", - "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.4.tgz", - "integrity": "sha512-HO3bosL84b2qWqI0q+kpT/OpRJwo0R4ivgmxaO848+bo10rc50SkPnrtwSFXttW0ym4np8jbJvLwk5NziB7jIw==", - "dev": true, - "dependencies": { - "cli": "~1.0.0", - "console-browserify": "1.1.x", - "exit": "0.1.x", - "htmlparser2": "3.8.x", - "lodash": "~4.17.21", - "minimatch": "~3.0.2", - "strip-json-comments": "1.0.x" - }, - "bin": { - "jshint": "bin/jshint" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema": { - "version": "0.2.3", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsprim": { - "version": "1.4.1", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "node_modules/jszip": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.0.tgz", - "integrity": "sha512-LDfVtOLtOxb9RXkYOwPyNBTQDL4eUbqahtoY6x07GiDJHwSYvn8sHHIw8wINImV3MqbMNve2gSuM1DDqEKk09Q==", - "dev": true, - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/jszip/node_modules/isarray": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/jszip/node_modules/readable-stream": { - "version": "2.3.7", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/jszip/node_modules/string_decoder": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/keyv": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/lines-and-columns": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", - "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/linkify-it": { - "version": "3.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "uc.micro": "^1.0.1" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash._reinterpolate": { - "version": "3.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.template": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", - "dev": true, - "dependencies": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "node_modules/lodash.templatesettings": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/magic-string": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", - "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/markdown-it-anchor": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-7.1.0.tgz", - "integrity": "sha512-loQggrwsIkkP7TOrESvmYkV2ikbQNNKhHcWyqC7/C2CmfHl1tkUizJJU8C5aGgg7J6oXVQJx17gk7i47tNn/lQ==", - "dev": true, - "peerDependencies": { - "markdown-it": "*" - } - }, - "node_modules/markdown-it/node_modules/entities": { - "version": "2.1.0", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/mdurl": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/mime-db": { - "version": "1.45.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.28", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.45.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", - "dev": true - }, - "node_modules/normalize-url": { - "version": "4.5.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-cancelable": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "dev": true, - "license": "(MIT AND Zlib)" - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/performance-now": { - "version": "2.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/psl": { - "version": "1.8.0", - "dev": true, - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.5.2", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/readable-stream": { - "version": "1.1.14", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/recast": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.19.1.tgz", - "integrity": "sha512-8FCjrBxjeEU2O6I+2hyHyBFH1siJbMBLwIRvVr1T3FD2cL754sOaJDsJ/8h3xYltasbJ8jqWRIhMuDGBSiSbjw==", - "dev": true, - "dependencies": { - "ast-types": "0.13.3", - "esprima": "~4.0.0", - "private": "^0.1.8", - "source-map": "~0.6.1" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", - "dev": true, - "dependencies": { - "is-finite": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/responselike": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "lowercase-keys": "^2.0.0" - } - }, - "node_modules/rollup": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.17.2.tgz", - "integrity": "sha512-qMNZdlQPCkWodrAZ3qnJtvCAl4vpQ8q77uEujVCCbC/6CLB7Lcmvjq7HyiOSnf4fxTT9XgsE36oLHJBH49xjqA==", - "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=14.18.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "dev": true - }, - "node_modules/sshpk": { - "version": "1.16.1", - "dev": true, - "license": "MIT", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string_decoder": { - "version": "0.10.31", - "dev": true, - "license": "MIT" - }, - "node_modules/strip-json-comments": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "bin": { - "strip-json-comments": "cli.js" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "dev": true, - "license": "Unlicense" - }, - "node_modules/uc.micro": { - "version": "1.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist-lint": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/uuid": { - "version": "3.4.0", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "license": "ISC" - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "dev": true, - "requires": { - "@babel/highlight": "^7.18.6" - } - }, - "@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", - "dev": true - }, - "@babel/core": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", - "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.0", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.0", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.0", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - } - }, - "@babel/generator": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", - "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", - "dev": true, - "requires": { - "@babel/types": "^7.21.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } - } - }, - "@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", - "dev": true, - "requires": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" - } - }, - "@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", - "dev": true, - "requires": { - "@babel/types": "^7.20.2" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", - "dev": true - }, - "@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", - "dev": true, - "requires": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" - } - }, - "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", - "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", - "dev": true - }, - "@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - } - }, - "@babel/traverse": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", - "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.1", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.2", - "@babel/types": "^7.21.2", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", - "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, - "@codemod/core": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@codemod/core/-/core-2.2.0.tgz", - "integrity": "sha512-H2Qa+hbHFf05xl4YpmvCoczgozQohl+cNiLlCDOmnwcSAgnurZCMRbAR7ppkXjoZ9LRosw3OyY9MTpiKhngWIg==", - "dev": true, - "requires": { - "@babel/core": "^7.20.12", - "@babel/generator": "^7.20.14", - "@codemod/parser": "^1.4.0", - "is-ci-cli": "^2.2.0", - "recast": "^0.19.0", - "resolve": "^1.12.0" - } - }, - "@codemod/parser": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@codemod/parser/-/parser-1.4.0.tgz", - "integrity": "sha512-Qi7PVgvA+RzM8Zzx0BnhH4CnWuwvD4A3/QJZGSJLELxJZwQVI3dT0o/7WLD9lLAkdz0n2Rep/REpfVdTNap/dg==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.15" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "@resugar/codemod-declarations-block-scope": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@resugar/codemod-declarations-block-scope/-/codemod-declarations-block-scope-1.0.3.tgz", - "integrity": "sha512-jnoNiz/PY4zZXPaLIeiKkGX6xau83Vn2lJRffIKuTPRMRTegFD2G7MmjxgZx9htJ/H9huqKJIhhqfj4VbuaDbA==", - "dev": true - }, - "@resugar/codemod-functions-arrow": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@resugar/codemod-functions-arrow/-/codemod-functions-arrow-1.0.2.tgz", - "integrity": "sha512-6dfHOEIxZw6sc0auiDpELDRYGFfVhrJwc6xN4JxpGE9vc3Hg954svu8MhedSV7VTVSClLv4CE0RvUOD+09DAuw==", - "dev": true, - "requires": { - "@resugar/helper-comments": "^1.0.0" - } - }, - "@resugar/codemod-modules-commonjs": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@resugar/codemod-modules-commonjs/-/codemod-modules-commonjs-1.0.5.tgz", - "integrity": "sha512-A1lBMZWNEUFpmSGgTRzEaRUOWxZ/aNjil2ekCfjiITRJ3UrvsSF7PIPgv5waKzsgimtiQ0Wm+fcqFko0itZ21A==", - "dev": true, - "requires": { - "@resugar/helper-comments": "^1.0.0" - } - }, - "@resugar/codemod-objects-concise": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@resugar/codemod-objects-concise/-/codemod-objects-concise-1.0.2.tgz", - "integrity": "sha512-Qf/zKkBzfGg1Q+2HSrssPllKYrwLxuDF/p39pz10keJtAhkYEcCbWcaHJRr5TgV9Pl9qnzzMImP9TG6KBwx7eA==", - "dev": true - }, - "@resugar/codemod-objects-destructuring": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@resugar/codemod-objects-destructuring/-/codemod-objects-destructuring-1.0.1.tgz", - "integrity": "sha512-xvJ1YAsV3/eSgL7BaSXRNnnz3B8f+dv+KNpi/07RF46L5ah3E8TLb2Ge/IgJB9lz/mhTDfB+pmFem9J3ndfN6w==", - "dev": true - }, - "@resugar/codemod-objects-shorthand": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@resugar/codemod-objects-shorthand/-/codemod-objects-shorthand-1.0.1.tgz", - "integrity": "sha512-1QZxpc2cX1DgxAWPDrp4l9VD1d+RZrqOXcXuckQmEIHr5TeSOhdBs2r3zFEbijxCTKD7fWsFgkas7LKh+KqGvQ==", - "dev": true, - "requires": { - "@resugar/helper-comments": "^1.0.0" + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0||^3.0.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "tslib": { + "optional": true + } } }, - "@resugar/codemod-strings-template": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@resugar/codemod-strings-template/-/codemod-strings-template-1.0.1.tgz", - "integrity": "sha512-ruVwOE67lmFGiyVr8I60l5lEDi0xKk1FYXyTGKMZdiiM2+W+L/qquOEqZmxOPA/xDNc5JG9VZXQ+4TZH9HqYxw==", - "dev": true - }, - "@resugar/helper-comments": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@resugar/helper-comments/-/helper-comments-1.0.2.tgz", - "integrity": "sha512-AXXKDyRcTN3naXhfvu/2mWX5OTc4RqlJIERCfnFs+M2VrawqoZU+oFS3L4ZsdP+/4rcofMCs9CMsYKc4S40PPQ==", - "dev": true - }, - "@rollup/pluginutils": { + "node_modules/@rollup/pluginutils": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", "dev": true, - "requires": { + "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "@sindresorhus/is": { + "node_modules/@sindresorhus/is": { "version": "3.1.2", - "dev": true + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.1.2.tgz", + "integrity": "sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } }, - "@szmarczak/http-timer": { + "node_modules/@szmarczak/http-timer": { "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", + "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", "dev": true, - "requires": { + "dependencies": { "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" } }, - "@types/cacheable-request": { + "node_modules/@types/cacheable-request": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", "dev": true, - "requires": { + "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "*", "@types/node": "*", "@types/responselike": "*" } }, - "@types/estree": { + "node_modules/@types/chrome": { + "version": "0.0.217", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.217.tgz", + "integrity": "sha512-q8fLzCCoHiR9gYRoqvrx12+HaJjRTqUom5Ks/wLSR8Ac83qAqWaA4NgUBUcDjM1O1ACczygxIHCEENXs1zmbqQ==", + "dev": true, + "dependencies": { + "@types/filesystem": "*", + "@types/har-format": "*" + } + }, + "node_modules/@types/estree": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", "dev": true }, - "@types/http-cache-semantics": { + "node_modules/@types/filesystem": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.32.tgz", + "integrity": "sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==", + "dev": true, + "dependencies": { + "@types/filewriter": "*" + } + }, + "node_modules/@types/filewriter": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.29.tgz", + "integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==", + "dev": true + }, + "node_modules/@types/har-format": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.10.tgz", + "integrity": "sha512-o0J30wqycjF5miWDKYKKzzOU1ZTLuA42HZ4HE7/zqTOc/jTLdQ5NhYWvsRQo45Nfi1KHoRdNhteSI4BAxTF1Pg==", + "dev": true + }, + "node_modules/@types/http-cache-semantics": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", "dev": true }, - "@types/keyv": { + "node_modules/@types/keyv": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "@types/node": { - "version": "14.14.20", + "node_modules/@types/node": { + "version": "18.14.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.5.tgz", + "integrity": "sha512-CRT4tMK/DHYhw1fcCEBwME9CSaZNclxfzVMe7GsO6ULSwsttbj70wSiX6rZdIjGblu93sTJxLdhNIT85KKI7Qw==", "dev": true }, - "@types/responselike": { + "node_modules/@types/responselike": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "add-variable-declarations": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/add-variable-declarations/-/add-variable-declarations-4.0.7.tgz", - "integrity": "sha512-SMjwvYGKrpk8CIN5K4LYpR54k9XpNmUdZLXOCvQvSvYnYTsvprouAuktct+ysSn1v2YVjQy4Du6JxwGhRV7Y1g==", - "dev": true, - "requires": { - "@babel/traverse": "^7.6.2", - "@babel/types": "^7.6.1", - "@codemod/parser": "^1.0.3", - "magic-string": "^0.25.3" - }, - "dependencies": { - "magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - } - } + "node_modules/@violentmonkey/types": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@violentmonkey/types/-/types-0.1.5.tgz", + "integrity": "sha512-pDj3TndOfXRWuyRLVlGCZbXDqh7L/Ca3qO8TDUWTtSuUPF+PkNKbMwuxPGrqmRFXn3V0hXp4wjCwzrDWyJCYiw==", + "dev": true }, - "ajv": { + "node_modules/ajv": { "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "requires": { + "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true - }, - "argparse": { + "node_modules/argparse": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "asn1": { + "node_modules/asn1": { "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "dev": true, - "requires": { + "dependencies": { "safer-buffer": "~2.1.0" } }, - "assert-plus": { + "node_modules/assert-plus": { "version": "1.0.0", - "dev": true - }, - "ast-types": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.3.tgz", - "integrity": "sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA==", - "dev": true + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "engines": { + "node": ">=0.8" + } }, - "asynckit": { + "node_modules/asynckit": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, - "automatic-semicolon-insertion": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/automatic-semicolon-insertion/-/automatic-semicolon-insertion-3.0.6.tgz", - "integrity": "sha512-M/+n8XFfrGzjgjXm+zzaCEy9nqiM+YkxQUN/P8Kfdu2i9FPvix7ece4B9kBN8Ca76UYiBJRQ+5u9DlLa7ownsA==", + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true, - "requires": { - "@babel/types": "^7.18.4" + "engines": { + "node": "*" } }, - "aws-sign2": { - "version": "0.7.0", - "dev": true - }, - "aws4": { + "node_modules/aws4": { "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==", "dev": true }, - "bcrypt-pbkdf": { + "node_modules/bcrypt-pbkdf": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, - "requires": { + "dependencies": { "tweetnacl": "^0.14.3" } }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "engines": { + "node": ">=10.6.0" } }, - "cacheable-lookup": { - "version": "5.0.4", - "dev": true - }, - "cacheable-request": { + "node_modules/cacheable-request": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", + "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", "dev": true, - "requires": { + "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", @@ -2933,411 +294,391 @@ "lowercase-keys": "^2.0.0", "normalize-url": "^4.1.0", "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "caniuse-lite": { - "version": "1.0.30001457", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", - "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", - "dev": true - }, - "caseless": { + "node_modules/caseless": { "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chrome-webstore-upload": { + "node_modules/chrome-webstore-upload": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/chrome-webstore-upload/-/chrome-webstore-upload-0.4.4.tgz", "integrity": "sha512-2afVQYCVfLAwYPPuCqKLivDl/CZcaRbmxHOq8B6oG1CbOPWncKl7yJR0G9LOu2wPfRprIEv3QGVQw3qRxuiBEw==", "dev": true, - "requires": { + "dependencies": { "got": "11.5.2" + }, + "engines": { + "node": ">=10.19.0" } }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cli": { + "node_modules/cli": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha512-41U72MB56TfUMGndAKK8vJ78eooOD4Z5NOL4xEfjc0c23s+6EYKXlXsmACBVclLP1yOfWCgEganVzddVrSNoTg==", "dev": true, - "requires": { + "dependencies": { "exit": "0.1.2", "glob": "^7.1.1" + }, + "engines": { + "node": ">=0.2.5" } }, - "clone-response": { + "node_modules/clone-response": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", "dev": true, - "requires": { + "dependencies": { "mimic-response": "^1.0.0" } }, - "coffee-lex": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/coffee-lex/-/coffee-lex-9.3.2.tgz", - "integrity": "sha512-lft4PwdLuqh3q50uR+yU5rpBKif1asJsXFPQ8AHDLs9woru6ENJ7jUVLGHk8cuCktvPFxaIzNeES9bNuSd89RA==", - "dev": true - }, - "coffeescript": { - "version": "1.12.7", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "combined-stream": { + "node_modules/combined-stream": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, - "requires": { + "dependencies": { "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "console-browserify": { + "node_modules/console-browserify": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha512-duS7VP5pvfsNLDvL1O4VOEbw37AI3A4ZUQYemvDlnpGrNu9tprR7BYWpDYwC0Xia0Zxz5ZupdiIrUp0GH1aXfg==", "dev": true, - "requires": { + "dependencies": { "date-now": "^0.1.4" } }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "core-util-is": { + "node_modules/core-util-is": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "dashdash": { + "node_modules/dashdash": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, - "requires": { + "dependencies": { "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" } }, - "date-now": { + "node_modules/date-now": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha512-AsElvov3LoNB7tf5k37H2jYSB+ZZPMT5sG2QjJCcdlV5chIv6htBUBUui2IKRjgtKAKtCBN7Zbwa+MtwLjSeNw==", "dev": true }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decaffeinate": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/decaffeinate/-/decaffeinate-8.1.3.tgz", - "integrity": "sha512-5IOmQ5Ym7tlurUeSJWDwCncbdl9w5SE+XnZXzONY5ZoVDjetlnJIs3cvOKzJNbtSJJo8GNteH3jBa274mkDhpw==", - "dev": true, - "requires": { - "@babel/types": "^7.12.6", - "@codemod/core": "^2.0.1", - "@codemod/parser": "^1.2.1", - "@resugar/codemod-declarations-block-scope": "^1.0.3", - "@resugar/codemod-functions-arrow": "^1.0.2", - "@resugar/codemod-modules-commonjs": "^1.0.5", - "@resugar/codemod-objects-concise": "^1.0.2", - "@resugar/codemod-objects-destructuring": "^1.0.1", - "@resugar/codemod-objects-shorthand": "^1.0.1", - "@resugar/codemod-strings-template": "^1.0.1", - "add-variable-declarations": "^4.0.7", - "automatic-semicolon-insertion": "^3.0.2", - "coffee-lex": "^9.3.2", - "decaffeinate-coffeescript": "1.12.7-patch.4", - "decaffeinate-coffeescript2": "2.2.1-patch.6", - "decaffeinate-parser": "^23.0.1", - "detect-indent": "^4.0.0", - "is-ci-cli": "^2.2.0", - "lines-and-columns": "^2.0.3", - "magic-string": "^0.26.2", - "mz": "^2.7.0", - "tslib": "^2.4.0" - } - }, - "decaffeinate-coffeescript": { - "version": "1.12.7-patch.4", - "resolved": "https://registry.npmjs.org/decaffeinate-coffeescript/-/decaffeinate-coffeescript-1.12.7-patch.4.tgz", - "integrity": "sha512-VLVyuAahCJMskqpbgcyg7MrxIVeooPDWoU8atvotH+OgLdIIJlKzFFHZoJCTaMdSxqtbCkli7dFzCeqnTixp5Q==", - "dev": true - }, - "decaffeinate-coffeescript2": { - "version": "2.2.1-patch.6", - "resolved": "https://registry.npmjs.org/decaffeinate-coffeescript2/-/decaffeinate-coffeescript2-2.2.1-patch.6.tgz", - "integrity": "sha512-R9QhWXDmRGs/uSPliv5n3FALNQwHYlkf6lhP5ZZ0QQ8NqZZJNfqLuiiSwe2t2/o6hHmIEkRSfQL/Ex1SEhSAHg==", - "dev": true - }, - "decaffeinate-parser": { - "version": "23.0.1", - "resolved": "https://registry.npmjs.org/decaffeinate-parser/-/decaffeinate-parser-23.0.1.tgz", - "integrity": "sha512-H0fuEdP2GWDhFIg4Ejlx2Loz6/yqEvWR3i2+mU8TNJkkJ9txd/4TEIdLcdMmBd9vw3eeBs0HHH+Yj8QbRO5aZQ==", + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, - "requires": { - "@babel/types": "^7.18.4", - "@codemod/parser": "^1.2.1", - "coffee-lex": "^9.3.1", - "decaffeinate-coffeescript": "^1.12.7-patch.4", - "decaffeinate-coffeescript2": "^2.2.1-patch.6", - "lines-and-columns": "^2.0.3" + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "decompress-response": { - "version": "6.0.0", + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, - "requires": { - "mimic-response": "^3.1.0" + "engines": { + "node": ">=10" }, - "dependencies": { - "mimic-response": { - "version": "3.1.0", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "defer-to-connect": { + "node_modules/defer-to-connect": { "version": "2.0.0", - "dev": true + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", + "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", + "dev": true, + "engines": { + "node": ">=10" + } }, - "delayed-stream": { + "node_modules/delayed-stream": { "version": "1.0.0", - "dev": true - }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha512-BDKtmHlOzwI7iRuEkhzsnPoi5ypEhWAJB5RvHWe1kMr06js3uK5B3734i3ui5Yd+wOJV1cpE4JnivPD283GU/A==", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, - "requires": { - "repeating": "^2.0.0" + "engines": { + "node": ">=0.4.0" } }, - "dom-serializer": { + "node_modules/dom-serializer": { "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", "dev": true, - "requires": { + "dependencies": { "domelementtype": "^2.0.1", "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.1.0", - "dev": true - }, - "entities": { - "version": "2.1.0", - "dev": true + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" } + ] + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "domelementtype": { + "node_modules/domelementtype": { "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", "dev": true }, - "domhandler": { + "node_modules/domhandler": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha512-q9bUwjfp7Eif8jWxxxPSykdRZAb6GkguBGSgvvCrhI9wB71W2K/Kvv4E61CF/mcCfnVJDeDWx/Vb/uAqbDj6UQ==", "dev": true, - "requires": { + "dependencies": { "domelementtype": "1" } }, - "domutils": { + "node_modules/domutils": { "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", "dev": true, - "requires": { + "dependencies": { "dom-serializer": "0", "domelementtype": "1" } }, - "ecc-jsbn": { + "node_modules/ecc-jsbn": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, - "requires": { + "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" } }, - "electron-to-chromium": { - "version": "1.4.308", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.308.tgz", - "integrity": "sha512-qyTx2aDFjEni4UnRWEME9ubd2Xc9c0zerTUl/ZinvD4QPsF0S7kJTV/Es/lPCTkNX6smyYar+z/n8Cl6pFr8yQ==", - "dev": true - }, - "end-of-stream": { + "node_modules/end-of-stream": { "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, - "requires": { + "dependencies": { "once": "^1.4.0" } }, - "entities": { + "node_modules/entities": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha512-LbLqfXgJMmy81t+7c14mnulFHJ170cM6E+0vMXR9k/ZiZwgX8i5pNgjTCX3SO4VeUsFLV+8InixoretwU+MjBQ==", "dev": true }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "esprima": { + "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } }, - "estree-walker": { + "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true }, - "exit": { + "node_modules/exit": { "version": "0.1.2", - "dev": true + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } }, - "extend": { + "node_modules/extend": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "extsprintf": { + "node_modules/extsprintf": { "version": "1.3.0", - "dev": true + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ] }, - "fast-deep-equal": { + "node_modules/fast-deep-equal": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "fast-json-stable-stringify": { + "node_modules/fast-json-stable-stringify": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "font-awesome": { + "node_modules/font-awesome": { "version": "4.7.0", - "dev": true + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==", + "dev": true, + "engines": { + "node": ">=0.10.3" + } }, - "forever-agent": { + "node_modules/forever-agent": { "version": "0.6.1", - "dev": true + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "engines": { + "node": "*" + } }, - "form-data": { + "node_modules/form-data": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, - "requires": { + "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" } }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "fsevents": { + "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "optional": true + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, - "function-bind": { + "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-stream": { + "node_modules/get-stream": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, - "requires": { + "dependencies": { "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "getpass": { + "node_modules/getpass": { "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, - "requires": { + "dependencies": { "assert-plus": "^1.0.0" } }, - "glob": { + "node_modules/glob": { "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, - "requires": { + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "got": { + "node_modules/got": { "version": "11.5.2", + "resolved": "https://registry.npmjs.org/got/-/got-11.5.2.tgz", + "integrity": "sha512-yUhpEDLeuGiGJjRSzEq3kvt4zJtAcjKmhIiwNp/eUs75tRlXfWcHo5tcBaMQtnjHWC7nQYT5HkY/l0QOQTkVww==", "dev": true, - "requires": { + "dependencies": { "@sindresorhus/is": "^3.0.0", "@szmarczak/http-timer": "^4.0.5", "@types/cacheable-request": "^6.0.1", @@ -3349,39 +690,55 @@ "lowercase-keys": "^2.0.0", "p-cancelable": "^2.0.0", "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" } }, - "har-schema": { + "node_modules/har-schema": { "version": "2.0.0", - "dev": true + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "har-validator": { + "node_modules/har-validator": { "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", "dev": true, - "requires": { + "dependencies": { "ajv": "^6.12.3", "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" } }, - "has": { + "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, - "requires": { + "dependencies": { "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "htmlparser2": { + "node_modules/htmlparser2": { "version": "3.8.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha512-hBxEg3CYXe+rPIua8ETe7tmG3XDn9B0edOE/e9wH2nLczxzgdu0m0aNHY+5wFZiviLWLdANPJTssa92dMcXQ5Q==", "dev": true, - "requires": { + "dependencies": { "domelementtype": "1", "domhandler": "2.3", "domutils": "1.5", @@ -3389,117 +746,104 @@ "readable-stream": "1.1" } }, - "http-cache-semantics": { + "node_modules/http-cache-semantics": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", "dev": true }, - "http-signature": { + "node_modules/http-signature": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "dev": true, - "requires": { + "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" } }, - "http2-wrapper": { + "node_modules/http2-wrapper": { "version": "1.0.0-beta.5.2", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", + "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", "dev": true, - "requires": { + "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" } }, - "immediate": { + "node_modules/immediate": { "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "dev": true }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, - "requires": { + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-ci-cli": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-ci-cli/-/is-ci-cli-2.2.0.tgz", - "integrity": "sha512-Xg97ZGDzU0a9gPTAli+TNegMk+PI3x0KLRYCfBa2LAboF1YyuA03Gwdc9vpu3VRNU+lFFNkvXnIQuJ0PgB120Q==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "is-ci": "^2.0.0" - } - }, - "is-core-module": { + "node_modules/is-core-module": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, - "requires": { + "dependencies": { "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true - }, - "is-typedarray": { + "node_modules/is-typedarray": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, - "isarray": { + "node_modules/isarray": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "isstream": { + "node_modules/isstream": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "jsbn": { + "node_modules/jsbn": { "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "jshint": { + "node_modules/jshint": { "version": "2.13.4", "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.4.tgz", "integrity": "sha512-HO3bosL84b2qWqI0q+kpT/OpRJwo0R4ivgmxaO848+bo10rc50SkPnrtwSFXttW0ym4np8jbJvLwk5NziB7jIw==", "dev": true, - "requires": { + "dependencies": { "cli": "~1.0.0", "console-browserify": "1.1.x", "exit": "0.1.x", @@ -3507,361 +851,387 @@ "lodash": "~4.17.21", "minimatch": "~3.0.2", "strip-json-comments": "1.0.x" + }, + "bin": { + "jshint": "bin/jshint" } }, - "json-buffer": { + "node_modules/json-buffer": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "json-schema": { + "node_modules/json-schema": { "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha512-a3xHnILGMtk+hDOqNwHzF6e2fNbiMrXZvxKQiEv2MlgQP+pjIOzqAmKYD2mDpXYE/44M7g+n9p2bKkYWDUcXCQ==", "dev": true }, - "json-schema-traverse": { + "node_modules/json-schema-traverse": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "json-stringify-safe": { + "node_modules/json-stringify-safe": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "jsprim": { + "node_modules/jsprim": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha512-4Dj8Rf+fQ+/Pn7C5qeEX02op1WfOss3PKTE9Nsop3Dx+6UPxlm1dr/og7o2cRa5hNN07CACr4NFzRLtj/rjWog==", "dev": true, - "requires": { + "engines": [ + "node >=0.6.0" + ], + "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.2.3", "verror": "1.10.0" } }, - "jszip": { + "node_modules/jszip": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.0.tgz", "integrity": "sha512-LDfVtOLtOxb9RXkYOwPyNBTQDL4eUbqahtoY6x07GiDJHwSYvn8sHHIw8wINImV3MqbMNve2gSuM1DDqEKk09Q==", "dev": true, - "requires": { + "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" - }, + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "dependencies": { - "isarray": { - "version": "1.0.0", - "dev": true - }, - "readable-stream": { - "version": "2.3.7", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" } }, - "keyv": { + "node_modules/keyv": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", "dev": true, - "requires": { + "dependencies": { "json-buffer": "3.0.1" } }, - "lie": { + "node_modules/lie": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "dev": true, - "requires": { + "dependencies": { "immediate": "~3.0.5" } }, - "lines-and-columns": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", - "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", - "dev": true - }, - "linkify-it": { + "node_modules/linkify-it": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==", "dev": true, - "requires": { + "dependencies": { "uc.micro": "^1.0.1" } }, - "lodash": { + "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "lodash._reinterpolate": { + "node_modules/lodash._reinterpolate": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", "dev": true }, - "lodash.template": { + "node_modules/lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", "dev": true, - "requires": { + "dependencies": { "lodash._reinterpolate": "^3.0.0", "lodash.templatesettings": "^4.0.0" } }, - "lodash.templatesettings": { - "version": "4.2.0", - "dev": true, - "requires": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", "dev": true, - "requires": { - "yallist": "^3.0.2" + "dependencies": { + "lodash._reinterpolate": "^3.0.0" } }, - "magic-string": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", - "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" + "engines": { + "node": ">=8" } }, - "markdown-it": { + "node_modules/markdown-it": { "version": "12.3.2", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dev": true, - "requires": { + "dependencies": { "argparse": "^2.0.1", "entities": "~2.1.0", "linkify-it": "^3.0.1", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" }, - "dependencies": { - "entities": { - "version": "2.1.0", - "dev": true - } + "bin": { + "markdown-it": "bin/markdown-it.js" } }, - "markdown-it-anchor": { + "node_modules/markdown-it-anchor": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-7.1.0.tgz", "integrity": "sha512-loQggrwsIkkP7TOrESvmYkV2ikbQNNKhHcWyqC7/C2CmfHl1tkUizJJU8C5aGgg7J6oXVQJx17gk7i47tNn/lQ==", "dev": true, - "requires": {} + "peerDependencies": { + "markdown-it": "*" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, - "mdurl": { + "node_modules/mdurl": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, - "mime-db": { + "node_modules/mime-db": { "version": "1.45.0", - "dev": true + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { + "node_modules/mime-types": { "version": "2.1.28", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", + "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", "dev": true, - "requires": { + "dependencies": { "mime-db": "1.45.0" + }, + "engines": { + "node": ">= 0.6" } }, - "mimic-response": { + "node_modules/mimic-response": { "version": "1.0.1", - "dev": true + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "minimatch": { + "node_modules/minimatch": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "requires": { + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "node_modules/normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", "dev": true, - "requires": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" + "engines": { + "node": ">=8" } }, - "node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", - "dev": true - }, - "normalize-url": { - "version": "4.5.0", - "dev": true - }, - "oauth-sign": { + "node_modules/oauth-sign": { "version": "0.9.0", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "engines": { + "node": "*" + } }, - "once": { + "node_modules/once": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "requires": { + "dependencies": { "wrappy": "1" } }, - "p-cancelable": { + "node_modules/p-cancelable": { "version": "2.0.0", - "dev": true + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "pako": { + "node_modules/pako": { "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-parse": { + "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "performance-now": { + "node_modules/performance-now": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "dev": true }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { + "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "process-nextick-args": { + "node_modules/process-nextick-args": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "psl": { + "node_modules/psl": { "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", "dev": true }, - "pump": { + "node_modules/pump": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, - "requires": { + "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, - "punycode": { + "node_modules/punycode": { "version": "2.1.1", - "dev": true + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "qs": { + "node_modules/qs": { "version": "6.5.2", - "dev": true + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true, + "engines": { + "node": ">=0.6" + } }, - "quick-lru": { + "node_modules/quick-lru": { "version": "5.1.1", - "dev": true + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "readable-stream": { + "node_modules/readable-stream": { "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", "dev": true, - "requires": { + "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", "isarray": "0.0.1", "string_decoder": "~0.10.x" } }, - "recast": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.19.1.tgz", - "integrity": "sha512-8FCjrBxjeEU2O6I+2hyHyBFH1siJbMBLwIRvVr1T3FD2cL754sOaJDsJ/8h3xYltasbJ8jqWRIhMuDGBSiSbjw==", - "dev": true, - "requires": { - "ast-types": "0.13.3", - "esprima": "~4.0.0", - "private": "^0.1.8", - "source-map": "~0.6.1" - } - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, - "request": { + "node_modules/request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", "dev": true, - "requires": { + "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", "caseless": "~0.12.0", @@ -3882,90 +1252,83 @@ "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" } }, - "resolve": { + "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, - "requires": { + "dependencies": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "resolve-alpn": { + "node_modules/resolve-alpn": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz", + "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==", "dev": true }, - "responselike": { + "node_modules/responselike": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", "dev": true, - "requires": { + "dependencies": { "lowercase-keys": "^2.0.0" } }, - "rollup": { + "node_modules/rollup": { "version": "3.17.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.17.2.tgz", "integrity": "sha512-qMNZdlQPCkWodrAZ3qnJtvCAl4vpQ8q77uEujVCCbC/6CLB7Lcmvjq7HyiOSnf4fxTT9XgsE36oLHJBH49xjqA==", "dev": true, - "requires": { + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { "fsevents": "~2.3.2" } }, - "safe-buffer": { + "node_modules/safe-buffer": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, - "safer-buffer": { + "node_modules/safer-buffer": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "setimmediate": { + "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", "dev": true }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "sshpk": { + "node_modules/sshpk": { "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "dev": true, - "requires": { + "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "bcrypt-pbkdf": "^1.0.0", @@ -3975,135 +1338,145 @@ "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" } }, - "string_decoder": { + "node_modules/string_decoder": { "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "dev": true }, - "strip-json-comments": { + "node_modules/strip-json-comments": { "version": "1.0.4", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha512-AOPG8EBc5wAikaG1/7uFCNFJwnKOuQwFTpYBdTW6OvWHeZBQBrAA/amefHGrEiOnCPcLFZK6FUPtWVKpQVIRgg==", "dev": true, - "requires": { - "has-flag": "^3.0.0" + "bin": { + "strip-json-comments": "cli.js" + }, + "engines": { + "node": ">=0.8.0" } }, - "supports-preserve-symlinks-flag": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "requires": { - "any-promise": "^1.0.0" - } - }, - "thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "dev": true, - "requires": { - "thenify": ">= 3.1.0 < 4" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "tough-cookie": { + "node_modules/tough-cookie": { "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, - "requires": { + "dependencies": { "psl": "^1.1.28", "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" } }, - "tslib": { + "node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, - "tunnel-agent": { + "node_modules/tunnel-agent": { "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, - "requires": { + "dependencies": { "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" } }, - "tweetnacl": { + "node_modules/tweetnacl": { "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, - "uc.micro": { - "version": "1.0.6", - "dev": true - }, - "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" } }, - "uri-js": { + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "node_modules/uri-js": { "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { + "dependencies": { "punycode": "^2.1.0" } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "uuid": { + "node_modules/uuid": { "version": "3.4.0", - "dev": true + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } }, - "verror": { + "node_modules/verror": { "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, - "requires": { + "engines": [ + "node >=0.6.0" + ], + "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true } } diff --git a/package.json b/package.json index d28238ca1d..e62997f6df 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,20 @@ { - "name": "4chan-X", - "description": "4chan X is a script that adds various features to anonymous imageboards.", + "name": "4chan-XT", + "description": "4chan XT is a script that adds various features to anonymous imageboards.", "meta": { - "name": "4chan X", - "path": "4chan-x", - "fork": "ccd0", - "page": "https://www.4chan-x.net/", - "downloads": "https://www.4chan-x.net/builds/", + "name": "4chan XT", + "path": "4chan-XT", + "fork": "TuxedoTako", + "page": "https://github.com/TuxedoTako/4chan-xt", + "downloads": "https://github.com/TuxedoTako/4chan-xt/releases", "oldVersions": "https://raw.githubusercontent.com/ccd0/4chan-x/", - "faq": "https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions", - "captchaFAQ": "https://github.com/ccd0/4chan-x/wiki/Captcha-FAQ", - "cssGuide": "https://github.com/ccd0/4chan-x/wiki/Styling-Guide", - "license": "https://github.com/ccd0/4chan-x/blob/master/LICENSE", - "changelog": "https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md", - "issues": "https://github.com/ccd0/4chan-x/issues", - "newIssue": "https://github.com/ccd0/4chan-x/issues", + "faq": "https://github.com/TuxedoTako/wiki/Frequently-Asked-Questions", + "captchaFAQ": "https://github.com/TuxedoTako/wiki/Captcha-FAQ", + "cssGuide": "https://github.com/TuxedoTako/wiki/Styling-Guide", + "license": "https://github.com/TuxedoTako/blob/master/LICENSE", + "changelog": "https://github.com/TuxedoTako/blob/master/CHANGELOG.md", + "issues": "https://github.com/TuxedoTako/issues", + "newIssue": "https://github.com/TuxedoTako/issues", "newIssueMaxLength": 8181, "alternatives": "https://www.4chan-x.net/4chan_alternatives.html", "appid": "lacclbnghgdicfifcamcmcnilckjamag", @@ -95,16 +95,18 @@ "GM.xmlHttpRequest" ], "min": { - "chrome": "33", - "firefox": "26", + "chrome": "80", + "firefox": "74", "greasemonkey": "1.14" } }, "devDependencies": { + "@rollup/plugin-typescript": "^11.0.0", "@rollup/pluginutils": "^5.0.2", + "@types/chrome": "^0.0.217", + "@types/node": "^18.14.5", + "@violentmonkey/types": "^0.1.5", "chrome-webstore-upload": "^0.4.4", - "coffeescript": "=1.12.7", - "decaffeinate": "^8.1.3", "esprima": "^4.0.1", "font-awesome": "=4.7.0", "jshint": "^2.13.4", @@ -113,7 +115,9 @@ "markdown-it": "^12.3.2", "markdown-it-anchor": "^7.1.0", "request": "^2.88.2", - "rollup": "^3.17.2" + "rollup": "^3.17.2", + "tslib": "^2.5.0", + "typescript": "^4.9.5" }, "repository": { "type": "git", @@ -131,7 +135,12 @@ "license": "MIT", "readmeFilename": "README.md", "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" }, - "type": "module" + "type": "module", + "scripts": { + "build": "node ./tools/rollup", + "build:beta": "node ./tools/rollup -beta", + "build:noupdate": "node ./tools/rollup -noupdate" + } } diff --git a/src/Archive/Redirect.js b/src/Archive/Redirect.js index c4a2aadb5e..342b76da8f 100644 --- a/src/Archive/Redirect.js +++ b/src/Archive/Redirect.js @@ -1,31 +1,35 @@ +import Notice from '../classes/Notice.js'; +import { Conf } from '../globals/globals.js'; +import $ from '../platform/$.js'; +import CrossOrigin from '../platform/CrossOrigin.js'; +import { DAY, dict } from '../platform/helpers.js'; +import archives from './archives.json'; /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -import archives from './archives.js'; var Redirect = { - // TODO check archives, init() { this.selectArchives(); if (Conf['archiveAutoUpdate']) { const now = Date.now(); - if (now - (2 * $.DAY) >= Conf['lastarchivecheck'] || Conf['lastarchivecheck'] > now) { return this.update(); } + if (now - (2 * DAY) >= Conf['lastarchivecheck'] || Conf['lastarchivecheck'] > now) { return this.update(); } } }, selectArchives() { let boardID, boards, data, files; const o = { - thread: $.dict(), - post: $.dict(), - file: $.dict() + thread: dict(), + post: dict(), + file: dict() }; - archives = $.dict(); + const archives = dict(); for (data of Conf['archives']) { var name, software, uid; for (var key of ['boards', 'files']) { @@ -104,15 +108,15 @@ var Redirect = { }, parse(responses, cb) { - archives = []; - const archiveUIDs = $.dict(); + const archives = []; + const archiveUIDs = dict(); for (var response of responses) { for (var data of response) { var uid = JSON.stringify(data.uid ?? data.name); if (uid in archiveUIDs) { $.extend(archiveUIDs[uid], data); } else { - archiveUIDs[uid] = $.dict.clone(data); + archiveUIDs[uid] = dict.clone(data); archives.push(data); } } @@ -238,3 +242,5 @@ var Redirect = { } } }; + +export default Redirect; diff --git a/src/Archive/archives.js b/src/Archive/archives.json similarity index 98% rename from src/Archive/archives.js rename to src/Archive/archives.json index 23a7cd49a0..6cade6eb88 100644 --- a/src/Archive/archives.js +++ b/src/Archive/archives.json @@ -1,4 +1,4 @@ -const archives = [{ +[{ "uid": 3, "name": "4plebs", "domain": "archive.4plebs.org", @@ -99,6 +99,4 @@ const archives = [{ "boards": ["bant", "c", "con", "e", "i", "n", "news", "out", "p", "pw", "qst", "toy", "vip", "vp", "vt", "w", "wg", "wsr"], "files": ["bant", "c", "e", "i", "n", "news", "out", "p", "pw", "qst", "toy", "vip", "vp", "vt", "w", "wg", "wsr"], "reports": true -}]; - -export default archives; +}] diff --git a/src/Filtering/Anonymize.js b/src/Filtering/Anonymize.js index 16cc8c9305..63398758ca 100644 --- a/src/Filtering/Anonymize.js +++ b/src/Filtering/Anonymize.js @@ -1,3 +1,6 @@ +import { Conf, doc } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -9,3 +12,4 @@ const Anonymize = { return $.addClass(doc, 'anonymize'); } }; +export default Anonymize; diff --git a/src/Filtering/Filter.js b/src/Filtering/Filter.js index 68db9d25f4..752c7fff1f 100644 --- a/src/Filtering/Filter.js +++ b/src/Filtering/Filter.js @@ -1,3 +1,18 @@ +import Callbacks from "../classes/Callbacks"; +import Notice from "../classes/Notice"; +import Config from "../config/Config"; +import Get from "../General/Get"; +import Settings from "../General/Settings"; +import { g, Conf, doc } from "../globals/globals"; +import Menu from "../Menu/Menu"; +import Unread from "../Monitoring/Unread"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import { dict } from "../platform/helpers"; +import QuoteYou from "../Quotelinks/QuoteYou"; +import PostHiding from "./PostHiding"; +import ThreadHiding from "./ThreadHiding"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -7,7 +22,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ var Filter = { - filters: $.dict(), + filters: dict(), init() { if (!['index', 'thread', 'catalog'].includes(g.VIEW) || !Conf['Filter']) { return; } if ((g.VIEW === 'catalog') && !Conf['Filter in Native Catalog']) { return; } @@ -126,7 +141,7 @@ var Filter = { let boards; if (!boardsRaw) { return false; } if (boards = Filter.parseBoardsMemo[boardsRaw]) { return boards; } - boards = $.dict(); + boards = dict(); let siteFilter = ''; for (var boardID of boardsRaw.split(',')) { if (boardID.includes(':')) { @@ -149,7 +164,7 @@ var Filter = { return boards; }, - parseBoardsMemo: $.dict(), + parseBoardsMemo: dict(), test(post, hideable=true) { if (post.filterResults) { return post.filterResults; } @@ -225,7 +240,7 @@ var Filter = { catalog() { let url; if (!(url = g.SITE.urls.catalogJSON?.(g.BOARD))) { return; } - Filter.catalogData = $.dict(); + Filter.catalogData = dict(); $.ajax(url, {onloadend: Filter.catalogParse}); return Callbacks.CatalogThreadNative.push({ @@ -500,3 +515,4 @@ var Filter = { } } }; +export default Filter; diff --git a/src/Filtering/PostHiding.js b/src/Filtering/PostHiding.js index f9d820d510..02cd5192aa 100644 --- a/src/Filtering/PostHiding.js +++ b/src/Filtering/PostHiding.js @@ -1,3 +1,12 @@ +import Callbacks from "../classes/Callbacks"; +import DataBoard from "../classes/DataBoard"; +import Get from "../General/Get"; +import UI from "../General/UI"; +import { g, Conf, doc } from "../globals/globals"; +import Menu from "../Menu/Menu"; +import $ from "../platform/$"; +import Recursive from "./Recursive"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -288,3 +297,4 @@ var PostHiding = { } } }; +export default PostHiding; diff --git a/src/Filtering/Recursive.js b/src/Filtering/Recursive.js index b570c4a026..8345402520 100644 --- a/src/Filtering/Recursive.js +++ b/src/Filtering/Recursive.js @@ -1,3 +1,7 @@ +import Callbacks from "../classes/Callbacks"; +import { g } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -57,3 +61,4 @@ var Recursive = { }); } }; +export default Recursive; diff --git a/src/Filtering/ThreadHiding.js b/src/Filtering/ThreadHiding.js index b1beb1f920..ad3275c059 100644 --- a/src/Filtering/ThreadHiding.js +++ b/src/Filtering/ThreadHiding.js @@ -1,3 +1,15 @@ +import Callbacks from "../classes/Callbacks"; +import DataBoard from "../classes/DataBoard"; +import Thread from "../classes/Thread"; +import Index from "../General/Index"; +import UI from "../General/UI"; +import { g, Conf, d, doc } from "../globals/globals"; +import Main from "../main/Main"; +import Menu from "../Menu/Menu"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import { dict } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -23,7 +35,7 @@ var ThreadHiding = { if (!$.hasStorage || (g.SITE.software !== 'yotsuba')) { return; } const hiddenThreads = ThreadHiding.db.get({ boardID: board.ID, - defaultValue: $.dict() + defaultValue: dict() }); for (var threadID in hiddenThreads) { hiddenThreads[threadID] = true; } return localStorage.setItem(`4chan-hide-t-${board}`, JSON.stringify(hiddenThreads)); @@ -272,3 +284,4 @@ var ThreadHiding = { } } }; +export default ThreadHiding; diff --git a/src/General/BoardConfig.js b/src/General/BoardConfig.js index 9f385f0c38..37af54eb14 100644 --- a/src/General/BoardConfig.js +++ b/src/General/BoardConfig.js @@ -1,3 +1,8 @@ +import Notice from "../classes/Notice"; +import { g, Conf } from "../globals/globals"; +import $ from "../platform/$"; +import { dict, HOUR } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -12,7 +17,7 @@ var BoardConfig = { let middle; if (g.SITE.software !== 'yotsuba') { return; } const now = Date.now(); - if (now - (2 * $.HOUR) >= ((middle = Conf['boardConfig'].lastChecked || 0)) || middle > now) { + if (now - (2 * HOUR) >= ((middle = Conf['boardConfig'].lastChecked || 0)) || middle > now) { return $.ajax(`${location.protocol}//a.4cdn.org/boards.json`, {onloadend: this.load}); } else { @@ -24,7 +29,7 @@ var BoardConfig = { load() { let boards; if ((this.status === 200) && this.response && this.response.boards) { - boards = $.dict(); + boards = dict(); for (var board of this.response.boards) { boards[board.board] = board; } @@ -98,3 +103,4 @@ var BoardConfig = { return (this.boards || Conf['boardConfig'].boards)?.[boardID]?.title || ''; } }; +export default BoardConfig; diff --git a/src/General/Get.js b/src/General/Get.js index f20c6ce08a..de30209f3f 100644 --- a/src/General/Get.js +++ b/src/General/Get.js @@ -1,3 +1,6 @@ +import { Conf, g } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -94,3 +97,4 @@ var Get = { }); } }; +export default Get; diff --git a/src/General/Header.js b/src/General/Header.js index 025f886125..4ee483a9c3 100644 --- a/src/General/Header.js +++ b/src/General/Header.js @@ -1,3 +1,17 @@ +import Redirect from "../Archive/Redirect"; +import Notice from "../classes/Notice"; +import { Conf, d, doc, g } from "../globals/globals"; +import Main from "../main/Main"; +import CatalogLinks from "../Miscellaneous/CatalogLinks"; +import ReplyPruning from "../Monitoring/ReplyPruning"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import BoardConfig from "./BoardConfig"; +import Get from "./Get"; +import Settings from "./Settings"; +import UI from "./UI"; +import meta from '../../package.json'; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -461,8 +475,7 @@ var Header = { }, setBarPosition(bottom) { - // TODO check if barPositionToggler exists - Header.barPositionToggler.checked = bottom; + if (Header.barPositionToggler) Header.barPositionToggler.checked = bottom; $.event('CloseMenu'); const args = bottom ? [ 'bottom-header', @@ -657,9 +670,12 @@ var Header = { break; } - // TODO meta const el = $.el('span', - {innerHTML: "meta.name needs your permission to show desktop notifications. [FAQ]
or "}); + {innerHTML: + `${meta.name} needs your permission to show desktop notifications. ` + + `[FAQ]` + + `
or ` + }); const [authorize, disable] = Array.from($$('button', el)); $.on(authorize, 'click', () => Notification.requestPermission(function(status) { Header.areNotificationsEnabled = status === 'granted'; @@ -673,3 +689,4 @@ var Header = { return notice = new Notice('info', el); } }; +export default Header; diff --git a/src/General/Index.js b/src/General/Index.js index f369fb26d8..43e0ffd322 100644 --- a/src/General/Index.js +++ b/src/General/Index.js @@ -6,8 +6,32 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ +import Callbacks from '../classes/Callbacks'; +import CatalogThread from '../classes/CatalogThread'; +import Notice from '../classes/Notice'; +import Post from '../classes/Post'; +import Thread from '../classes/Thread'; +import Config from '../config/Config'; +import Filter from '../Filtering/Filter'; +import PostHiding from '../Filtering/PostHiding'; +import ThreadHiding from '../Filtering/ThreadHiding'; +import Main from '../main/Main'; +import CatalogLinks from '../Miscellaneous/CatalogLinks'; +import RelativeDates from '../Miscellaneous/RelativeDates'; +import ThreadWatcher from '../Monitoring/ThreadWatcher'; +import $$ from '../platform/$$'; +import $ from '../platform/$'; +import QuotePreview from '../Quotelinks/QuotePreview'; +import { c, Conf, d, doc, g } from '../globals/globals'; +import Header from './Header'; +import UI from './UI'; +import Menu from '../Menu/Menu'; + import NavLinksPage from './Index/NavLinks.html'; import PageListPage from './Index/PageList.html'; +import BoardConfig from './BoardConfig'; +import Get from './Get'; +import { dict, SECOND } from '../platform/helpers'; var Index = { showHiddenThreads: false, @@ -23,7 +47,7 @@ var Index = { // For IndexRefresh events $.one(d, '4chanXInitFinished', this.cb.initFinished); - $.on(d, 'PostsInserted', this.cb.postsInserted); + $.on(d, 'PostsInserted', this.cb.postsInserted); if (!this.enabledOn(g.BOARD)) { return; } @@ -69,7 +93,7 @@ var Index = { // Header "Index Navigation" submenu const entries = []; - this.inputs = (inputs = $.dict()); + this.inputs = (inputs = dict()); for (name in Config.Index) { var arr = Config.Index[name]; if (arr instanceof Array) { @@ -233,7 +257,7 @@ var Index = { if (notify) { return; } notify = true; new Notice('info', "Last page reached.", 2); - return setTimeout(reset, 3 * $.SECOND); + return setTimeout(reset, 3 * SECOND); }; })(), @@ -345,10 +369,10 @@ var Index = { }, perBoardSort() { - Conf['Index Sort'] = this.checked ? $.dict() : ''; + Conf['Index Sort'] = this.checked ? dict() : ''; Index.saveSort(); for (let i = 0; i < 2; i++) { - Conf[`Last Long Reply Thresholds ${i}`] = this.checked ? $.dict() : ''; + Conf[`Last Long Reply Thresholds ${i}`] = this.checked ? dict() : ''; Index.saveLastLongThresholds(i); } }, @@ -721,13 +745,16 @@ var Index = { if (Conf['Index Refresh Notifications']) { // Optional notification for manual refreshes if (!Index.notice) { Index.notice = new Notice('info', 'Refreshing index...'); } - if (!Index.nTimeout) { Index.nTimeout = setTimeout(() => // TODO check if notice exists - Index.notice.el.lastElementChild.textContent += ' (disable JSON Index if this takes too long)' - , 3 * $.SECOND); } + if (!Index.nTimeout) { Index.nTimeout = setTimeout(() => { + if (Index.notice) { + Index.notice.el.lastElementChild.textContent += ' (disable JSON Index if this takes too long)'; + } + } + , 3 * SECOND); } } else { // Also display notice if Index Refresh is taking too long if (!Index.nTimeout) { Index.nTimeout = setTimeout(() => Index.notice || (Index.notice = new Notice('info', 'Refreshing index... (disable JSON Index if this takes too long)')) - , 3 * $.SECOND); } + , 3 * SECOND); } } // Hard refresh in case of incomplete page load. @@ -760,7 +787,7 @@ var Index = { if (notice) { notice.setType('warning'); notice.el.lastElementChild.textContent = err; - setTimeout(notice.close, $.SECOND); + setTimeout(notice.close, SECOND); } else { new Notice('warning', err, 1); } @@ -779,7 +806,7 @@ var Index = { if (notice) { notice.setType('error'); notice.el.lastElementChild.textContent = 'Index refresh failed.'; - setTimeout(notice.close, $.SECOND); + setTimeout(notice.close, SECOND); } else { new Notice('error', 'Index refresh failed.', 1); } @@ -790,7 +817,7 @@ var Index = { if (Conf['Index Refresh Notifications']) { notice.setType('success'); notice.el.lastElementChild.textContent = 'Index refreshed!'; - setTimeout(notice.close, $.SECOND); + setTimeout(notice.close, SECOND); } else { notice.close(); } @@ -813,10 +840,10 @@ var Index = { Index.threadsNumPerPage = pages[0]?.threads.length || 1; Index.liveThreadData = pages.reduce(((arr, next) => arr.concat(next.threads)), []); Index.liveThreadIDs = Index.liveThreadData.map(data => data.no); - Index.liveThreadDict = $.dict(); - Index.threadPosition = $.dict(); - Index.parsedThreads = $.dict(); - Index.replyData = $.dict(); + Index.liveThreadDict = dict(); + Index.threadPosition = dict(); + Index.parsedThreads = dict(); + Index.replyData = dict(); for (let i = 0; i < Index.liveThreadData.length; i++) { var obj, results; var data = Index.liveThreadData[i]; @@ -1203,3 +1230,4 @@ var Index = { return true; } }; +export default Index; diff --git a/src/General/Settings.js b/src/General/Settings.tsx similarity index 93% rename from src/General/Settings.js rename to src/General/Settings.tsx index 4f89d39ce4..c3c1fdbe4a 100644 --- a/src/General/Settings.js +++ b/src/General/Settings.tsx @@ -6,20 +6,41 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -import SettingsPage from './Settings/Settings.html'; +import SettingsPage from './Settings/SettingsHtml'; import FilterGuidePage from './Settings/Filter-guide.html'; import SaucePage from './Settings/Sauce.html'; import AdvancedPage from './Settings/Advanced.html'; import KeybindsPage from './Settings/Keybinds.html'; import FilterSelectPage from './Settings/Filter-select.html'; +import Redirect from '../Archive/Redirect'; +import DataBoard from '../classes/DataBoard'; +import Notice from '../classes/Notice'; +import Config from '../config/Config'; +import ImageHost from '../Images/ImageHost'; +import CustomCSS from '../Miscellaneous/CustomCSS'; +import FileInfo from '../Miscellaneous/FileInfo'; +import Keybinds from '../Miscellaneous/Keybinds'; +import Time from '../Miscellaneous/Time'; +import Favicon from '../Monitoring/Favicon'; +import ThreadUpdater from '../Monitoring/ThreadUpdater'; +import Unread from '../Monitoring/Unread'; +import $$ from '../platform/$$'; +import $ from '../platform/$'; +import meta from '../../package.json'; +import { c, Conf, d, doc, g } from '../globals/globals'; +import Header from './Header'; +import h, { hFragment } from '../globals/jsx'; +import { dict, platform } from '../platform/helpers'; var Settings = { + dialog: undefined as HTMLDivElement | undefined, + init() { // 4chan X settings link const link = $.el('a', { className: 'settings-link fa fa-wrench', textContent: 'Settings', - title: '<%= meta.name %> Settings', + title: `${meta.name} Settings`, href: 'javascript:;' } ); @@ -63,8 +84,8 @@ var Settings = { $.event('CloseMenu'); Settings.dialog = (dialog = $.el('div', - {id: 'overlay'} - , { innerHTML: SettingsPage })); + { id: 'overlay' } + , SettingsPage)); $.on($('.export', dialog), 'click', Settings.export); $.on($('.import', dialog), 'click', Settings.import); @@ -135,7 +156,7 @@ var Settings = { const why = $.cantSet ? 'save your settings' : 'synchronize settings between tabs'; return cb($.el('li', { textContent: `\ -<%= meta.name %> needs local storage to ${why}. +${meta.name} needs local storage to ${why}. Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's privacy settings (may be listed as part of "local data" or "cookies").\ ` } @@ -147,10 +168,10 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri return $.onExists(doc, '.adg-rects > .desktop', ad => $.onExists(ad, 'iframe', function() { const url = Redirect.to('thread', {boardID: 'qa', threadID: 362590}); return cb($.el('li', - { innerHTML: - 'To protect yourself from malicious ads,' + - ' you should block ads on 4chan.' - } + <> + To protect yourself from malicious ads, + you should block ads on 4chan. + ) ); })); @@ -173,8 +194,8 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri } $.add(section, warnings); - const items = $.dict(); - const inputs = $.dict(); + const items = dict(); + const inputs = dict(); const addCheckboxes = function(root, obj) { const containers = [root]; return (() => { @@ -184,7 +205,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri if (arr instanceof Array) { var description = arr[1]; var div = $.el('div', - {innerHTML: ': ${description}'}); + { innerHTML: `: ${description}` }); div.dataset.name = key; var input = $('input', div); $.on(input, 'change', $.cb.checked); @@ -209,7 +230,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri for (var keyFS in Config.main) { var obj = Config.main[keyFS]; var fs = $.el('fieldset', - {innerHTML: '${keyFS}'}); + { innerHTML: `${keyFS}` }); addCheckboxes(fs, obj); if (keyFS === 'Posting and Captchas') { $.add(fs, $.el('p', @@ -227,7 +248,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri if ($.perProtocolSettings || (location.protocol !== 'https:')) { $('div[data-name="Redirect to HTTPS"]', section).hidden = true; } - if ($.platform !== 'crx') { + if (platform !== 'crx') { $('div[data-name="Work around CORB Bug"]', section).hidden = true; } @@ -242,7 +263,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri const div = $.el('div', {innerHTML: ': Clear manually-hidden threads and posts on all boards. Reload the page to apply.'}); const button = $('button', div); - $.get({hiddenThreads: $.dict(), hiddenPosts: $.dict()}, function({hiddenThreads, hiddenPosts}) { + $.get({hiddenThreads: dict(), hiddenPosts: $.dict()}, function({hiddenThreads, hiddenPosts}) { let board, ID, site, thread; let hiddenNum = 0; for (ID in hiddenThreads) { @@ -281,7 +302,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri }); $.on(button, 'click', function() { this.textContent = 'Hidden: 0'; - return $.get('hiddenThreads', $.dict(), function({hiddenThreads}) { + return $.get('hiddenThreads', dict(), function({hiddenThreads}) { if ($.hasStorage && (g.SITE.software === 'yotsuba')) { let boardID; for (boardID in hiddenThreads['4chan.org']?.boards) { @@ -299,7 +320,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri export() { // Make sure to export the most recent data, but don't overwrite existing `Conf` object. - const Conf2 = $.dict(); + const Conf2 = dict(); $.extend(Conf2, Conf); return $.get(Conf2, function(Conf2) { // Don't export cached JSON data. @@ -312,7 +333,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri const blob = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json'}); const url = URL.createObjectURL(blob); const a = $.el('a', { - download: `<%= meta.name %> v${g.VERSION}-${data.date}.json`, + download: `${meta.name} v${g.VERSION}-${data.date}.json`, href: url } ); @@ -339,7 +360,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri const reader = new FileReader(); reader.onload = function(e) { try { - return Settings.loadSettings($.dict.json(e.target.result), function(err) { + return Settings.loadSettings(dict.json(e.target.result), function (err) { if (err) { return output.textContent = 'Import failed due to an error.'; } else if (confirm('Import successful. Reload now?')) { @@ -455,12 +476,12 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri } } if (data.WatchedThreads) { - data.Conf['watchedThreads'] = $.dict.clone({'4chan.org': {boards: {}}}); + data.Conf['watchedThreads'] = dict.clone({ '4chan.org': { boards: {} } }); for (var boardID in data.WatchedThreads) { var threads = data.WatchedThreads[boardID]; for (var threadID in threads) { var threadData = threads[threadID]; - (data.Conf['watchedThreads']['4chan.org'].boards[boardID] || (data.Conf['watchedThreads']['4chan.org'].boards[boardID] = $.dict()))[threadID] = {excerpt: threadData.textContent}; + (data.Conf['watchedThreads']['4chan.org'].boards[boardID] || (data.Conf['watchedThreads']['4chan.org'].boards[boardID] = dict()))[threadID] = { excerpt: threadData.textContent }; } } } @@ -470,7 +491,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri upgrade(data, version) { let corrupted, key, val; - const changes = $.dict(); + const changes = dict(); const set = (key, value) => data[key] = (changes[key] = value); const setD = function(key, value) { if (data[key] == null) { return set(key, value); } @@ -684,7 +705,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri } } if ((data['siteSoftware'] != null) && (data['siteProperties'] == null)) { - const siteProperties = $.dict(); + const siteProperties = dict(); for (var line of data['siteSoftware'].split('\n')) { var [hostname, software] = Array.from(line.split(' ')); siteProperties[hostname] = {software}; @@ -745,7 +766,7 @@ mu-replace sp-replace tv-replace vp-replace -[external-text:"FAQ","<%= meta.faq %>"]\ +[external-text:"FAQ","${meta.faq}"]\ ` ); } @@ -831,7 +852,7 @@ vp-replace return; } const filterTypes = Object.keys(Config.filter).filter(x => x !== 'general').map((x, i) => ({ - innerHTML: '?{i}{,}${x}' + innerHTML: (i ? "," : "") + `${E(x)}` })); $.extend(div, { innerHTML: FilterGuidePage }); return $('.warning', div).hidden = Conf['Filter']; @@ -853,7 +874,7 @@ vp-replace $.extend(section, { innerHTML: AdvancedPage }); for (var warning of $$('.warning', section)) { warning.hidden = Conf[warning.dataset.feature]; } - const inputs = $.dict(); + const inputs = dict(); for (input of $$('[name]', section)) { inputs[input.name] = input; } @@ -864,7 +885,7 @@ vp-replace return $.id('lastarchivecheck').textContent = 'never'; }); - const items = $.dict(); + const items = dict(); for (name in inputs) { input = inputs[name]; if (!['Interval', 'Custom CSS'].includes(name)) { @@ -909,7 +930,7 @@ vp-replace $.on(customCSS, 'change', Settings.togglecss); $.on(applyCSS, 'click', () => CustomCSS.update()); - const itemsArchive = $.dict(); + const itemsArchive = dict(); for (name of ['archives', 'selectedArchives', 'lastarchivecheck']) { itemsArchive[name] = Conf[name]; } $.get(itemsArchive, function(itemsArchive) { $.extend(Conf, itemsArchive); @@ -943,7 +964,7 @@ vp-replace $.rmAll(boardSelect); $.rmAll(tbody); - const archBoards = $.dict(); + const archBoards = dict(); for (var {uid, name, boards, files, software} of Conf['archives']) { if (!['fuuka', 'foolfuuka'].includes(software)) { continue; } for (boardID of boards) { @@ -1039,7 +1060,7 @@ vp-replace saveSelectedArchive() { return $.get('selectedArchives', Conf['selectedArchives'], ({selectedArchives}) => { - (selectedArchives[this.dataset.boardid] || (selectedArchives[this.dataset.boardid] = $.dict()))[this.dataset.type] = JSON.parse(this.value); + (selectedArchives[this.dataset.boardid] || (selectedArchives[this.dataset.boardid] = dict()))[this.dataset.type] = JSON.parse(this.value); $.set('selectedArchives', selectedArchives); Conf['selectedArchives'] = selectedArchives; return Redirect.selectArchives(); @@ -1108,12 +1129,12 @@ vp-replace $('.warning', section).hidden = Conf['Keybinds']; const tbody = $('tbody', section); - const items = $.dict(); - const inputs = $.dict(); + const items = dict(); + const inputs = dict(); for (key in Config.hotkeys) { var arr = Config.hotkeys[key]; var tr = $.el('tr', - {innerHTML: '${arr[1]}'}); + { innerHTML: `${arr[1]}` }); var input = $('input', tr); input.name = key; input.spellcheck = false; @@ -1141,3 +1162,4 @@ vp-replace return $.cb.value.call(this); } }; +export default Settings; diff --git a/src/General/Settings/Settings.html b/src/General/Settings/Settings.html deleted file mode 100644 index 79d337643f..0000000000 --- a/src/General/Settings/Settings.html +++ /dev/null @@ -1,17 +0,0 @@ -
- -
-
diff --git a/src/General/Settings/SettingsHtml.tsx b/src/General/Settings/SettingsHtml.tsx new file mode 100644 index 0000000000..b435b6cdeb --- /dev/null +++ b/src/General/Settings/SettingsHtml.tsx @@ -0,0 +1,26 @@ +import { g } from "../../globals/globals"; +import h from "../../globals/jsx"; +import meta from '../../../package.json'; + +// \u00A0 is non breaking space +const separator = '\u00A0|\u00A0'; + +const settingsHtml =
+ +
+
; + +export default settingsHtml; diff --git a/src/General/Test.js b/src/General/Test.js index 79a83f6e1a..6b68aace89 100644 --- a/src/General/Test.js +++ b/src/General/Test.js @@ -5,8 +5,21 @@ * DS205: Consider reworking code to avoid use of IIFEs * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -// <% if (readJSON('/.tests_enabled')) { %> -var Test = { + +import Notice from "../classes/Notice"; +import Post from "../classes/Post"; +import Config from "../config/Config"; +import Filter from "../Filtering/Filter"; +import ImageHost from "../Images/ImageHost"; +import Keybinds from "../Miscellaneous/Keybinds"; +import Unread from "../Monitoring/Unread"; +import $$ from "../platform/$$"; +import $ from "../platform/$"; +import Header from "./Header"; +import { g, Conf, c } from "../globals/globals"; +import Menu from "../Menu/Menu"; + +const Test = { init() { if ((g.SITE.software !== 'yotsuba') || !['index', 'thread'].includes(g.VIEW)) { return; } @@ -237,4 +250,4 @@ var Test = { } } }; -// <% } %> +export default Test; diff --git a/src/General/UI.js b/src/General/UI.js index 9e3139912b..c1096ece93 100644 --- a/src/General/UI.js +++ b/src/General/UI.js @@ -1,3 +1,9 @@ +import { Conf, d } from "../globals/globals"; +import Main from "../main/Main"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import Header from "./Header"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -278,7 +284,7 @@ var Menu = (function() { return Menu; })(); -var dragstart = function(e) { +export var dragstart = function (e) { let isTouching; if ((e.type === 'mousedown') && (e.button !== 0)) { return; } // not LMB // prevent text selection @@ -324,7 +330,7 @@ var dragstart = function(e) { } }; -var touchmove = function(e) { +export var touchmove = function (e) { for (var touch of e.changedTouches) { if (touch.identifier === this.identifier) { drag.call(this, touch); @@ -333,7 +339,7 @@ var touchmove = function(e) { } }; -var drag = function(e) { +export var drag = function (e) { const {clientX, clientY} = e; let left = clientX - this.dx; @@ -369,7 +375,7 @@ var drag = function(e) { return style.bottom = bottom; }; -var touchend = function(e) { +export var touchend = function (e) { for (var touch of e.changedTouches) { if (touch.identifier === this.identifier) { dragend.call(this); @@ -378,7 +384,7 @@ var touchend = function(e) { } }; -var dragend = function() { +export var dragend = function () { if (this.isTouching) { $.off(d, 'touchmove', this.move); $.off(d, 'touchend touchcancel', this.up); @@ -389,7 +395,7 @@ var dragend = function() { return $.set(`${this.id}.position`, this.style.cssText); }; -const hoverstart = function({root, el, latestEvent, endEvents, height, width, cb, noRemove}) { +const hoverstart = function ({ root, el, latestEvent, endEvents, height, width, cb, noRemove }) { const rect = root.getBoundingClientRect(); const o = { root, @@ -428,7 +434,7 @@ const hoverstart = function({root, el, latestEvent, endEvents, height, width, cb hoverstart.padding = 25; -var hover = function(e) { +export var hover = function (e) { this.latestEvent = e; const height = (this.height || this.el.offsetHeight) + hoverstart.padding; const width = (this.width || this.el.offsetWidth); @@ -452,7 +458,7 @@ var hover = function(e) { return style.right = right; }; -var hoverend = function(e) { +export var hoverend = function (e) { if (((e.type === 'keydown') && (e.keyCode !== 13)) || (e.target.nodeName === "TEXTAREA")) { return; } if (!this.noRemove) { $.rm(this.el); } $.off(this.root, this.endEvents, this.hoverend); @@ -463,7 +469,7 @@ var hoverend = function(e) { if (this.cb) { return this.cb.call(this); } }; -const checkbox = function(name, text, checked) { +export const checkbox = function (name, text, checked) { if (checked == null) { checked = Conf[name]; } const label = $.el('label'); const input = $.el('input', {type: 'checkbox', name, checked}); @@ -477,3 +483,4 @@ const UI = { hover: hoverstart, checkbox }; +export default UI; diff --git a/src/Images/FappeTyme.js b/src/Images/FappeTyme.js index 8620a13e0e..5704f9f210 100644 --- a/src/Images/FappeTyme.js +++ b/src/Images/FappeTyme.js @@ -1,3 +1,9 @@ +import Callbacks from "../classes/Callbacks"; +import Header from "../General/Header"; +import UI from "../General/UI"; +import { Conf, doc, g } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -83,3 +89,4 @@ var FappeTyme = { if (type === 'werk') { return $.cb.checked.call(this.nodes[type]); } } }; +export default FappeTyme; diff --git a/src/Images/Gallery.js b/src/Images/Gallery.js index cbccd67bc5..d04c63a35c 100644 --- a/src/Images/Gallery.js +++ b/src/Images/Gallery.js @@ -6,6 +6,20 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ import galleryPage from './Gallery/Gallery.html'; +import $ from '../platform/$'; +import Callbacks from '../classes/Callbacks'; +import Notice from '../classes/Notice'; +import Main from '../main/Main'; +import Keybinds from '../Miscellaneous/Keybinds'; +import $$ from '../platform/$$'; +import ImageCommon from './ImageCommon'; +import Sauce from './Sauce'; +import Volume from './Volume'; +import Header from '../General/Header'; +import { Conf, d, doc, g } from '../globals/globals'; +import UI from '../General/UI'; +import Get from '../General/Get'; +import { debounce, dict, SECOND } from '../platform/helpers'; var Gallery = { init() { @@ -64,7 +78,7 @@ var Gallery = { Gallery.images = []; const nodes = (Gallery.nodes = {}); - Gallery.fileIDs = $.dict(); + Gallery.fileIDs = dict(); Gallery.slideshow = false; nodes.el = (dialog = $.el('div', @@ -280,7 +294,7 @@ var Gallery = { }, startTimer() { - return Gallery.timeoutID = setTimeout(Gallery.checkTimer, Gallery.delay * $.SECOND); + return Gallery.timeoutID = setTimeout(Gallery.checkTimer, Gallery.delay * SECOND); }, setupTimer() { @@ -399,7 +413,7 @@ var Gallery = { rotateLeft() { return Gallery.cb.rotate(270); }, rotateRight() { return Gallery.cb.rotate(90); }, - rotate: $.debounce(100, function(delta) { + rotate: debounce(100, function(delta) { const {current} = Gallery.nodes; if (current.nodeName === 'IFRAME') { return; } current.dataRotate = ((current.dataRotate || 0) + delta) % 360; @@ -431,7 +445,7 @@ var Gallery = { return (this.checked ? $.addClass : $.rmClass)(doc, `gal-${this.name.toLowerCase().replace(/\s+/g, '-')}`); }, - setHeight: $.debounce(100, function() { + setHeight: debounce(100, function () { let dim, margin, minHeight; const {current, frame} = Gallery.nodes; const {style} = current; @@ -504,3 +518,4 @@ var Gallery = { } } }; +export default Gallery; diff --git a/src/Images/ImageCommon.js b/src/Images/ImageCommon.js index f4a1b4062d..bc36f436bd 100644 --- a/src/Images/ImageCommon.js +++ b/src/Images/ImageCommon.js @@ -1,3 +1,11 @@ +import Redirect from "../Archive/Redirect"; +import Notice from "../classes/Notice"; +import { g, Conf, d } from "../globals/globals"; +import $ from "../platform/$"; +import CrossOrigin from "../platform/CrossOrigin"; +import ImageHost from "./ImageHost"; +import Volume from "./Volume"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -144,3 +152,4 @@ var ImageCommon = { }); } }; +export default ImageCommon; diff --git a/src/Images/ImageExpand.js b/src/Images/ImageExpand.js index 6a6071472e..7c1e59a794 100644 --- a/src/Images/ImageExpand.js +++ b/src/Images/ImageExpand.js @@ -1,3 +1,15 @@ +import Callbacks from "../classes/Callbacks"; +import Config from "../config/Config"; +import Get from "../General/Get"; +import Header from "../General/Header"; +import UI from "../General/UI"; +import { Conf, d, doc, g } from "../globals/globals"; +import Nav from "../Miscellaneous/Nav"; +import $ from "../platform/$"; +import { SECOND } from "../platform/helpers"; +import ImageCommon from "./ImageCommon"; +import Volume from "./Volume"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -338,7 +350,7 @@ var ImageExpand = { if (ImageCommon.isFromArchive(this)) { return ImageExpand.contract(post); } - return ImageCommon.error(this, post, post.file, 10 * $.SECOND, function(URL) { + return ImageCommon.error(this, post, post.file, 10 * SECOND, function(URL) { if (post.file.isExpanding || post.file.isExpanded) { ImageExpand.contract(post); if (URL) { return ImageExpand.expand(post, URL); } @@ -383,3 +395,4 @@ var ImageExpand = { } } }; +export default ImageExpand; diff --git a/src/Images/ImageHost.js b/src/Images/ImageHost.js index cfc7a6f4d3..1af2e15929 100644 --- a/src/Images/ImageHost.js +++ b/src/Images/ImageHost.js @@ -1,3 +1,7 @@ +import Callbacks from "../classes/Callbacks"; +import { Conf, g } from "../globals/globals"; +import $$ from "../platform/$$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -49,3 +53,4 @@ var ImageHost = { } } }; +export default ImageHost; diff --git a/src/Images/ImageHover.js b/src/Images/ImageHover.js index f87a68583f..3edff195b8 100644 --- a/src/Images/ImageHover.js +++ b/src/Images/ImageHover.js @@ -1,3 +1,12 @@ +import Callbacks from "../classes/Callbacks"; +import Header from "../General/Header"; +import UI from "../General/UI"; +import { g, Conf, doc } from "../globals/globals"; +import $ from "../platform/$"; +import { SECOND } from "../platform/helpers"; +import ImageCommon from "./ImageCommon"; +import Volume from "./Volume"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -93,7 +102,7 @@ var ImageHover = { error(post, file) { return function() { if (ImageCommon.decodeError(this, file)) { return; } - return ImageCommon.error(this, post, file, 3 * $.SECOND, URL => { + return ImageCommon.error(this, post, file, 3 * SECOND, URL => { if (URL) { return this.src = URL + (this.src === URL ? '?' + Date.now() : ''); } else { @@ -102,3 +111,4 @@ var ImageHover = { }); }; } }; +export default ImageHover; diff --git a/src/Images/ImageLoader.js b/src/Images/ImageLoader.js index 648ac5c04c..b5ed88bed2 100644 --- a/src/Images/ImageLoader.js +++ b/src/Images/ImageLoader.js @@ -1,3 +1,8 @@ +import Callbacks from "../classes/Callbacks"; +import Header from "../General/Header"; +import { g, Conf, d } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -133,3 +138,4 @@ var ImageLoader = { }); } }; +export default ImageLoader; diff --git a/src/Images/Metadata.js b/src/Images/Metadata.js index f01652d80b..de064144c0 100644 --- a/src/Images/Metadata.js +++ b/src/Images/Metadata.js @@ -1,3 +1,9 @@ +import $ from "../platform/$"; +import Callbacks from "../classes/Callbacks"; +import CrossOrigin from "../platform/CrossOrigin"; +import { Conf, d, g } from "../globals/globals"; +import Get from "../General/Get"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -87,3 +93,4 @@ var Metadata = { return null; } }; +export default Metadata; diff --git a/src/Images/RevealSpoilers.js b/src/Images/RevealSpoilers.js index efd69bcefd..414fe4ee63 100644 --- a/src/Images/RevealSpoilers.js +++ b/src/Images/RevealSpoilers.js @@ -1,3 +1,6 @@ +import Callbacks from "../classes/Callbacks"; +import { g, Conf } from "../globals/globals"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -31,3 +34,4 @@ const RevealSpoilers = { } } }; +export default RevealSpoilers; diff --git a/src/Images/Sauce.js b/src/Images/Sauce.js index 027fb98ee9..5d1646b598 100644 --- a/src/Images/Sauce.js +++ b/src/Images/Sauce.js @@ -1,3 +1,10 @@ +import Callbacks from "../classes/Callbacks"; +import Notice from "../classes/Notice"; +import Filter from "../Filtering/Filter"; +import { g, Conf, doc } from "../globals/globals"; +import $ from "../platform/$"; +import { dict } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -35,7 +42,7 @@ var Sauce = { parseLink(link) { if (!(link = link.trim())) { return null; } - const parts = $.dict(); + const parts = dict(); const iterable = link.split(/;(?=(?:text|boards|types|regexp|sandbox):?)/); for (let i = 0; i < iterable.length; i++) { var part = iterable[i]; @@ -75,7 +82,7 @@ var Sauce = { createSauceLink(link, post, file) { let a, matches, needle; const ext = file.url.match(/[^.]*$/)[0]; - const parts = $.dict(); + const parts = dict(); $.extend(parts, link); if (!!parts['boards'] && !parts['boards'][`${post.siteID}/${post.boardID}`] && !parts['boards'][`${post.siteID}/*`]) { return null; } @@ -168,3 +175,4 @@ var Sauce = { semi() { return ';'; } } }; +export default Sauce; diff --git a/src/Images/Volume.js b/src/Images/Volume.js index 41b11fbcdd..1cb018b125 100644 --- a/src/Images/Volume.js +++ b/src/Images/Volume.js @@ -1,3 +1,10 @@ +import Callbacks from "../classes/Callbacks"; +import Config from "../config/Config"; +import Header from "../General/Header"; +import UI from "../General/UI"; +import { g, Conf, E } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -10,14 +17,12 @@ var Volume = { $.sync('Allow Sound', function(x) { Conf['Allow Sound'] = x; - // TODO check if inputs exits - return Volume.inputs.unmute.checked = x; + if (Volume.inputs) Volume.inputs.unmute.checked = x; }); $.sync('Default Volume', function(x) { Conf['Default Volume'] = x; - // TODO check if inputs exits - return Volume.inputs.volume.value = x; + if (Volume.inputs) Volume.inputs.volume.value = x; }); if (Conf['Mouse Wheel Volume']) { @@ -110,3 +115,4 @@ var Volume = { return e.preventDefault(); } }; +export default Volume; diff --git a/src/Linkification/Embedding.js b/src/Linkification/Embedding.js index 31f0dca63b..6bdbd583fc 100644 --- a/src/Linkification/Embedding.js +++ b/src/Linkification/Embedding.js @@ -1,3 +1,14 @@ +import Get from '../General/Get'; +import Header from '../General/Header'; +import UI from '../General/UI'; +import { g, Conf, d, doc } from '../globals/globals'; +import ImageHost from '../Images/ImageHost'; +import Main from '../main/Main'; +import $ from '../platform/$'; +import $$ from '../platform/$$'; +import CrossOrigin from '../platform/CrossOrigin'; +import { dict } from '../platform/helpers'; +import EmbeddingPage from './Embedding/Embed.html'; /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -6,12 +17,11 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -import EmbeddingPage from './Ebedding/embedding.html'; var Embedding = { init() { if (!['index', 'thread', 'archive'].includes(g.VIEW) || !Conf['Linkify'] || (!Conf['Embedding'] && !Conf['Link Title'] && !Conf['Cover Preview'])) { return; } - this.types = $.dict(); + this.types = dict(); for (var type of this.ordered_types) { this.types[type.key] = type; } if (Conf['Embedding'] && (g.VIEW !== 'archive')) { @@ -689,3 +699,4 @@ var Embedding = { } ] }; +export default Embedding; diff --git a/src/Linkification/Linkify.js b/src/Linkification/Linkify.js index b785cdf9b5..20096ce39b 100644 --- a/src/Linkification/Linkify.js +++ b/src/Linkification/Linkify.js @@ -1,3 +1,12 @@ +import Callbacks from "../classes/Callbacks"; +// import Test from "../General/Test"; +import { g, Conf } from "../globals/globals"; +import ImageHost from "../Images/ImageHost"; +import ExpandComment from "../Miscellaneous/ExpandComment"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import Embedding from "./Embedding"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -94,11 +103,13 @@ var Linkify = { if (Linkify.regString.test(word)) { links.push(Linkify.makeRange(node, endNode, index, length)); - } + // <% if (readJSON('/.tests_enabled')) { %> - // Test.assert -> - // word is links[links.length-1].toString() + // if (links.length) { + // Test.assert(() => word === links[links.length - 1]?.toString()); + // } // <% } %> + } if (!test.lastIndex || (node !== endNode)) { break; } } @@ -196,3 +207,4 @@ aero|asia|biz|cat|com|coop|dance|info|int|jobs|mobi|moe|museum|name|net|org|post return a; } }; +export default Linkify; diff --git a/src/Menu/ArchiveLink.js b/src/Menu/ArchiveLink.js index 06b0f57141..e65fd107e8 100644 --- a/src/Menu/ArchiveLink.js +++ b/src/Menu/ArchiveLink.js @@ -1,3 +1,9 @@ +import $ from "../platform/$"; +import Redirect from "../Archive/Redirect"; +import Filter from "../Filtering/Filter"; +import { g, Conf } from "../globals/globals"; +import Menu from "./Menu"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -76,3 +82,4 @@ const ArchiveLink = { }; } }; +export default ArchiveLink; diff --git a/src/Menu/CopyTextLink.js b/src/Menu/CopyTextLink.js index c622ee8681..d1597f0570 100644 --- a/src/Menu/CopyTextLink.js +++ b/src/Menu/CopyTextLink.js @@ -1,3 +1,7 @@ +import { g, Conf, d } from "../globals/globals"; +import $ from "../platform/$"; +import Menu from "./Menu"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -39,3 +43,4 @@ var CopyTextLink = { return $.rm(el); } }; +export default CopyTextLink; diff --git a/src/Menu/DeleteLink.js b/src/Menu/DeleteLink.js index 6714e88088..e34106bc91 100644 --- a/src/Menu/DeleteLink.js +++ b/src/Menu/DeleteLink.js @@ -1,3 +1,9 @@ +import Notice from "../classes/Notice"; +import { g, Conf } from "../globals/globals"; +import $ from "../platform/$"; +import { dict } from "../platform/helpers"; +import QR from "../Posting/QR"; +import Menu from "./Menu"; /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -5,7 +11,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ var DeleteLink = { - auto: [$.dict(), $.dict()], + auto: [dict(), dict()], init() { if (!['index', 'thread'].includes(g.VIEW) || !Conf['Menu'] || !Conf['Delete Link']) { return; } @@ -147,7 +153,7 @@ var DeleteLink = { }, cooldown: { - seconds: $.dict(), + seconds: dict(), start(post, seconds) { // Already counting. @@ -176,3 +182,4 @@ var DeleteLink = { } } }; +export default DeleteLink; diff --git a/src/Menu/DownloadLink.js b/src/Menu/DownloadLink.js index b6b5c96268..84deee5b19 100644 --- a/src/Menu/DownloadLink.js +++ b/src/Menu/DownloadLink.js @@ -1,3 +1,8 @@ +import { g, Conf } from "../globals/globals"; +import ImageCommon from "../Images/ImageCommon"; +import $ from "../platform/$"; +import Menu from "./Menu"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -28,3 +33,4 @@ const DownloadLink = { }); } }; +export default DownloadLink; diff --git a/src/Menu/Menu.js b/src/Menu/Menu.js index 0c191645f4..e62c15aaf5 100644 --- a/src/Menu/Menu.js +++ b/src/Menu/Menu.js @@ -1,3 +1,8 @@ +import Callbacks from "../classes/Callbacks"; +import UI from "../General/UI"; +import { g, Conf } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -50,3 +55,4 @@ var Menu = { return button; } }; +export default Menu; diff --git a/src/Menu/ReportLink.js b/src/Menu/ReportLink.js index 7ddcb1a4a4..30c3c8652b 100644 --- a/src/Menu/ReportLink.js +++ b/src/Menu/ReportLink.js @@ -1,3 +1,7 @@ +import { g, Conf } from "../globals/globals"; +import $ from "../platform/$"; +import Menu from "./Menu"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -37,3 +41,4 @@ var ReportLink = { return window.open(url, id, set); } }; +export default ReportLink; diff --git a/src/Miscellaneous/AntiAutoplay.js b/src/Miscellaneous/AntiAutoplay.js index 2ca75e2d83..1f0fbe8070 100644 --- a/src/Miscellaneous/AntiAutoplay.js +++ b/src/Miscellaneous/AntiAutoplay.js @@ -1,3 +1,8 @@ +import Callbacks from "../classes/Callbacks"; +import { Conf, doc } from "../globals/globals"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -44,3 +49,4 @@ var AntiAutoplay = { return $.addClass(el, 'autoplay-removed'); } }; +export default AntiAutoplay; diff --git a/src/Miscellaneous/Banner.js b/src/Miscellaneous/Banner.js index 8894bf443b..790ceb5ed0 100644 --- a/src/Miscellaneous/Banner.js +++ b/src/Miscellaneous/Banner.js @@ -1,3 +1,11 @@ +import DataBoard from "../classes/DataBoard"; +import { Conf, d, g } from "../globals/globals"; +import Main from "../main/Main"; +import Unread from "../Monitoring/Unread"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import { dict } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -103,7 +111,7 @@ var Banner = { } }, - original: $.dict(), + original: dict(), custom(child) { let data; @@ -125,3 +133,4 @@ var Banner = { } } }; +export default Banner; diff --git a/src/Miscellaneous/CatalogLinks.js b/src/Miscellaneous/CatalogLinks.js index 46b605df58..06977553e7 100644 --- a/src/Miscellaneous/CatalogLinks.js +++ b/src/Miscellaneous/CatalogLinks.js @@ -1,3 +1,16 @@ +import Callbacks from "../classes/Callbacks"; +import Filter from "../Filtering/Filter"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import meta from '../../package.json'; +import Index from "../General/Index"; +import Site from "../site/Site"; +import Header from "../General/Header"; +import { g, Conf } from "../globals/globals"; +import UI from "../General/UI"; +import Get from "../General/Get"; +import { dict } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -28,14 +41,14 @@ var CatalogLinks = { var catalogLink = link.parentNode.cloneNode(true); var link2 = catalogLink.firstElementChild; link2.href = catalogURL; - link2.textContent = link2.hostname === location.hostname ? '<%= meta.name %> Catalog' : 'External Catalog'; + link2.textContent = link2.hostname === location.hostname ? `${meta.name} Catalog` : 'External Catalog'; $.after(link.parentNode, [$.tn(' '), catalogLink]); } } }); } - if ((g.SITE.software === 'yotsuba') && Conf['JSON Index'] && Conf['Use <%= meta.name %> Catalog']) { + if ((g.SITE.software === 'yotsuba') && Conf['JSON Index'] && Conf[`Use ${meta.name} Catalog`]) { Callbacks.Post.push({ name: 'Catalog Link Rewrite', cb: this.node @@ -111,12 +124,12 @@ var CatalogLinks = { }, externalParse() { - CatalogLinks.externalList = $.dict(); + CatalogLinks.externalList = dict(); for (var line of Conf['externalCatalogURLs'].split('\n')) { if (line[0] === '#') { continue; } var url = line.split(';')[0]; var boards = Filter.parseBoards(line.match(/;boards:([^;]+)/)?.[1] || '*'); - var excludes = Filter.parseBoards(line.match(/;exclude:([^;]+)/)?.[1]) || $.dict(); + var excludes = Filter.parseBoards(line.match(/;exclude:([^;]+)/)?.[1]) || dict(); for (var board in boards) { if (!excludes[board] && !excludes[board.split('/')[0] + '/*']) { CatalogLinks.externalList[board] = url; @@ -143,7 +156,7 @@ var CatalogLinks = { let external, nativeCatalog; if (Conf['External Catalog'] && (external = CatalogLinks.external(board))) { return external; - } else if (Index.enabledOn(board) && Conf['Use <%= meta.name %> Catalog']) { + } else if (Index.enabledOn(board) && Conf[`Use ${meta.name} Catalog`]) { return CatalogLinks.jsonIndex(board, '#catalog'); } else if (nativeCatalog = Get.url('catalog', board)) { return nativeCatalog; @@ -160,3 +173,4 @@ var CatalogLinks = { } } }; +export default CatalogLinks; diff --git a/src/Miscellaneous/CustomCSS.js b/src/Miscellaneous/CustomCSS.js index 22db6a43b2..2e0a074c61 100644 --- a/src/Miscellaneous/CustomCSS.js +++ b/src/Miscellaneous/CustomCSS.js @@ -1,3 +1,7 @@ +import $ from "../platform/$"; +import CSS from "../css/CSS"; +import { Conf } from "../globals/globals"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -27,3 +31,4 @@ const CustomCSS = { return this.style.textContent = CSS.sub(Conf['usercss']); } }; +export default CustomCSS; diff --git a/src/Miscellaneous/ExpandComment.js b/src/Miscellaneous/ExpandComment.js index 5b42093078..33537d5e4d 100644 --- a/src/Miscellaneous/ExpandComment.js +++ b/src/Miscellaneous/ExpandComment.js @@ -1,3 +1,9 @@ +import Callbacks from "../classes/Callbacks"; +import Get from "../General/Get"; +import { g, Conf } from "../globals/globals"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -94,3 +100,4 @@ var ExpandComment = { } } }; +export default ExpandComment;; diff --git a/src/Miscellaneous/ExpandThread.js b/src/Miscellaneous/ExpandThread.js index 6ca2f3252a..dab38f5300 100644 --- a/src/Miscellaneous/ExpandThread.js +++ b/src/Miscellaneous/ExpandThread.js @@ -1,3 +1,13 @@ +import Callbacks from "../classes/Callbacks"; +import Post from "../classes/Post"; +import Get from "../General/Get"; +import Index from "../General/Index"; +import { g, Conf, d } from "../globals/globals"; +import Main from "../main/Main"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import { dict } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -5,7 +15,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ var ExpandThread = { - statuses: $.dict(), + statuses: dict(), init() { if (!((g.VIEW === 'index') && Conf['Thread Expansion'])) { return; } if (Conf['JSON Index']) { @@ -156,3 +166,4 @@ var ExpandThread = { } } }; +export default ExpandThread; diff --git a/src/Miscellaneous/FileInfo.js b/src/Miscellaneous/FileInfo.js deleted file mode 100644 index d03c625747..0000000000 --- a/src/Miscellaneous/FileInfo.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -var FileInfo = { - init() { - if (!['index', 'thread', 'archive'].includes(g.VIEW) || !Conf['File Info Formatting']) { return; } - - return Callbacks.Post.push({ - name: 'File Info Formatting', - cb: this.node - }); - }, - - node() { - if (!this.file) { return; } - if (this.isClone) { - let a; - for (a of $$('.file-info .download-button', this.file.text)) { - $.on(a, 'click', ImageCommon.download); - } - for (a of $$('.file-info .quick-filter-md5', this.file.text)) { - $.on(a, 'click', Filter.quickFilterMD5); - } - return; - } - - const oldInfo = $.el('span', {className: 'fileText-original'}); - $.prepend(this.file.link.parentNode, oldInfo); - $.add(oldInfo, [this.file.link.previousSibling, this.file.link, this.file.link.nextSibling]); - - const info = $.el('span', {className: 'file-info'}); - FileInfo.format(Conf['fileInfo'], this, info); - return $.prepend(this.file.text, info); - }, - - format(formatString, post, outputNode) { - let a; - const output = []; - formatString.replace(/%(.)|[^%]+/g, function(s, c) { - output.push($.hasOwn(FileInfo.formatters, c) ? - FileInfo.formatters[c].call(post) - : - {innerHTML: E(s)} - ); - return ''; - }); - $.extend(outputNode, {innerHTML: E.cat(output)}); - for (a of $$('.download-button', outputNode)) { - $.on(a, 'click', ImageCommon.download); - } - for (a of $$('.quick-filter-md5', outputNode)) { - $.on(a, 'click', Filter.quickFilterMD5); - } - }, - - formatters: { - t() { return {innerHTML: E(this.file.url.match(/[^/]*$/)[0])}; }, - T() { return {innerHTML: "" + (FileInfo.formatters.t.call(this)).innerHTML + ""}; }, - l() { return {innerHTML: "" + (FileInfo.formatters.n.call(this)).innerHTML + ""}; }, - L() { return {innerHTML: "" + (FileInfo.formatters.N.call(this)).innerHTML + ""}; }, - n() { - const fullname = this.file.name; - const shortname = SW.yotsuba.Build.shortFilename(this.file.name, this.isReply); - if (fullname === shortname) { - return {innerHTML: E(fullname)}; - } else { - return {innerHTML: "" + E(shortname) + "" + E(fullname) + ""}; - } - }, - N() { return {innerHTML: E(this.file.name)}; }, - d() { return {innerHTML: ""}; }, - f() { return {innerHTML: ""}; }, - p() { return {innerHTML: ((this.file.isSpoiler) ? "Spoiler, " : "")}; }, - s() { return {innerHTML: E(this.file.size)}; }, - B() { return {innerHTML: E(Math.round(this.file.sizeInBytes)) + " Bytes"}; }, - K() { return {innerHTML: E(Math.round(this.file.sizeInBytes/1024)) + " KB"}; }, - M() { return {innerHTML: E(Math.round(this.file.sizeInBytes/1048576*100)/100) + " MB"}; }, - r() { return {innerHTML: E(this.file.dimensions || "PDF")}; }, - g() { return {innerHTML: ((this.file.tag) ? ", " + E(this.file.tag) : "")}; }, - '%'() { return {innerHTML: "%"}; } - } -}; diff --git a/src/Miscellaneous/FileInfo.tsx b/src/Miscellaneous/FileInfo.tsx new file mode 100644 index 0000000000..2d967704e2 --- /dev/null +++ b/src/Miscellaneous/FileInfo.tsx @@ -0,0 +1,99 @@ +import Callbacks from "../classes/Callbacks"; +import Filter from "../Filtering/Filter"; +import { g, Conf, E } from "../globals/globals"; +import h, { isEscaped } from "../globals/jsx"; +import ImageCommon from "../Images/ImageCommon"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import SW from "../site/SW"; + +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +var FileInfo = { + init() { + if (!['index', 'thread', 'archive'].includes(g.VIEW) || !Conf['File Info Formatting']) { return; } + + return Callbacks.Post.push({ + name: 'File Info Formatting', + cb: this.node + }); + }, + + node() { + if (!this.file) { return; } + if (this.isClone) { + let a; + for (a of $$('.file-info .download-button', this.file.text)) { + $.on(a, 'click', ImageCommon.download); + } + for (a of $$('.file-info .quick-filter-md5', this.file.text)) { + $.on(a, 'click', Filter.quickFilterMD5); + } + return; + } + + const oldInfo = $.el('span', {className: 'fileText-original'}); + $.prepend(this.file.link.parentNode, oldInfo); + $.add(oldInfo, [this.file.link.previousSibling, this.file.link, this.file.link.nextSibling]); + + const info = $.el('span', {className: 'file-info'}); + FileInfo.format(Conf['fileInfo'], this, info); + return $.prepend(this.file.text, info); + }, + + format(formatString, post, outputNode) { + let a; + const output = []; + formatString.replace(/%(.)|[^%]+/g, function(s, c) { + output.push($.hasOwn(FileInfo.formatters, c) ? + FileInfo.formatters[c].call(post) + : + {innerHTML: E(s)} + ); + return ''; + }); + $.extend(outputNode, {innerHTML: E.cat(output)}); + for (a of $$('.download-button', outputNode)) { + $.on(a, 'click', ImageCommon.download); + } + for (a of $$('.quick-filter-md5', outputNode)) { + $.on(a, 'click', Filter.quickFilterMD5); + } + }, + + formatters: { + t() { return { innerHTML: E(this.file.url.match(/[^/]*$/)[0]), [isEscaped]: true }; }, + T() { return {FileInfo.formatters.t.call(this)} }, + l() { return {FileInfo.formatters.n.call(this)} }, + L() { return {FileInfo.formatters.N.call(this)} }, + n() { + const fullname = this.file.name; + const shortname = SW.yotsuba.Build.shortFilename(this.file.name, this.isReply); + if (fullname === shortname) { + return { innerHTML: E(fullname), [isEscaped]: true }; + } else { + return + {shortname} + {fullname} + ; + } + }, + N() { return { innerHTML: E(this.file.name), [isEscaped]: true }; }, + d() { return }, + f() { + return { innerHTML: "", [isEscaped]: true }; + }, + p() { return { innerHTML: ((this.file.isSpoiler) ? "Spoiler, " : ""), [isEscaped]: true }; }, + s() { return { innerHTML: E(this.file.size), [isEscaped]: true }; }, + B() { return { innerHTML: Math.round(this.file.sizeInBytes) + " Bytes", [isEscaped]: true }; }, + K() { return { innerHTML: (Math.round(this.file.sizeInBytes / 1024)) + " KB", [isEscaped]: true }; }, + M() { return { innerHTML: (Math.round(this.file.sizeInBytes / 1048576 * 100) / 100) + " MB", [isEscaped]: true }; }, + r() { return { innerHTML: E(this.file.dimensions || "PDF"), [isEscaped]: true }; }, + g() { return { innerHTML: ((this.file.tag) ? ", " + E(this.file.tag) : ""), [isEscaped]: true }; }, + '%'() { return { innerHTML: "%", [isEscaped]: true }; } + } +}; +export default FileInfo; diff --git a/src/Miscellaneous/Flash.js b/src/Miscellaneous/Flash.js index d377a7e613..dd8f54612d 100644 --- a/src/Miscellaneous/Flash.js +++ b/src/Miscellaneous/Flash.js @@ -1,3 +1,6 @@ +import { g, Conf } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -21,3 +24,4 @@ var Flash = { } } }; +export default Flash; diff --git a/src/Miscellaneous/Fourchan.js b/src/Miscellaneous/Fourchan.js index 43e8c198ec..a0311cef4d 100644 --- a/src/Miscellaneous/Fourchan.js +++ b/src/Miscellaneous/Fourchan.js @@ -1,3 +1,11 @@ +import Callbacks from "../classes/Callbacks"; +import BoardConfig from "../General/BoardConfig"; +import { d, doc, g } from "../globals/globals"; +import Main from "../main/Main"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import ExpandComment from "./ExpandComment"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -111,3 +119,4 @@ var Fourchan = { return cb(); } }; +export default Fourchan; diff --git a/src/Miscellaneous/IDColor.js b/src/Miscellaneous/IDColor.js index 840d61149c..52716fe132 100644 --- a/src/Miscellaneous/IDColor.js +++ b/src/Miscellaneous/IDColor.js @@ -1,3 +1,8 @@ +import Callbacks from "../classes/Callbacks"; +import { g, Conf } from "../globals/globals"; +import $ from "../platform/$"; +import { dict } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -6,7 +11,7 @@ var IDColor = { init() { if (!['index', 'thread'].includes(g.VIEW) || !Conf['Color User IDs']) { return; } - this.ids = $.dict(); + this.ids = dict(); this.ids['Heaven'] = [0, 0, 0, '#fff']; return Callbacks.Post.push({ @@ -51,3 +56,4 @@ var IDColor = { return this.ids[uid] = rgb; } }; +export default IDColor; diff --git a/src/Miscellaneous/IDHighlight.js b/src/Miscellaneous/IDHighlight.js index 8d83307309..8b8471ec17 100644 --- a/src/Miscellaneous/IDHighlight.js +++ b/src/Miscellaneous/IDHighlight.js @@ -1,3 +1,7 @@ +import Callbacks from "../classes/Callbacks"; +import { g } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -32,3 +36,4 @@ var IDHighlight = { return g.posts.forEach(IDHighlight.set); }; } }; +export default IDHighlight; diff --git a/src/Miscellaneous/IDPostCount.js b/src/Miscellaneous/IDPostCount.js index caebe8f274..b2182ee05f 100644 --- a/src/Miscellaneous/IDPostCount.js +++ b/src/Miscellaneous/IDPostCount.js @@ -1,3 +1,8 @@ +import Callbacks from "../classes/Callbacks"; +import Get from "../General/Get"; +import { g, Conf } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -31,3 +36,4 @@ var IDPostCount = { return this.title = `${n} post${n === 1 ? '' : 's'} by this ID`; } }; +export default IDPostCount; diff --git a/src/Miscellaneous/Keybinds.js b/src/Miscellaneous/Keybinds.js index d2c605bf12..6fa2aee58e 100644 --- a/src/Miscellaneous/Keybinds.js +++ b/src/Miscellaneous/Keybinds.js @@ -1,3 +1,29 @@ +import Notice from "../classes/Notice"; +import Config from "../config/Config"; +import Filter from "../Filtering/Filter"; +import ThreadHiding from "../Filtering/ThreadHiding"; +import BoardConfig from "../General/BoardConfig"; +import Get from "../General/Get"; +import Header from "../General/Header"; +import Index from "../General/Index"; +import Settings from "../General/Settings"; +import { Conf, d, g } from "../globals/globals"; +import FappeTyme from "../Images/FappeTyme"; +import Gallery from "../Images/Gallery"; +import ImageExpand from "../Images/ImageExpand"; +import Embedding from "../Linkification/Embedding"; +import ThreadUpdater from "../Monitoring/ThreadUpdater"; +import ThreadWatcher from "../Monitoring/ThreadWatcher"; +import UnreadIndex from "../Monitoring/UnreadIndex"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import QR from "../Posting/QR"; +import QuoteThreading from "../Quotelinks/QuoteThreading"; +import QuoteYou from "../Quotelinks/QuoteYou"; +import CatalogLinks from "./CatalogLinks"; +import ExpandThread from "./ExpandThread"; +import Nav from "./Nav"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -447,3 +473,4 @@ var Keybinds = { } } }; +export default Keybinds; diff --git a/src/Miscellaneous/ModContact.js b/src/Miscellaneous/ModContact.js index 3137e59443..b44efd531d 100644 --- a/src/Miscellaneous/ModContact.js +++ b/src/Miscellaneous/ModContact.js @@ -1,3 +1,7 @@ +import $ from "../platform/$"; +import Callbacks from "../classes/Callbacks"; +import { g } from "../globals/globals"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -40,3 +44,4 @@ var ModContact = { qa: {innerHTML: "Moving a thread to /qa/ does not imply mods will read it. If you wish to contact mods, use feedback (https://www.4chan.org/feedback) or IRC (https://www.4chan-x.net/4chan-irc.html)."} } }; +export default ModContact; diff --git a/src/Miscellaneous/Nav.js b/src/Miscellaneous/Nav.js index f8fd4fff41..cc486898c4 100644 --- a/src/Miscellaneous/Nav.js +++ b/src/Miscellaneous/Nav.js @@ -1,3 +1,9 @@ +import Get from "../General/Get"; +import Header from "../General/Header"; +import { g, Conf, d, doc } from "../globals/globals"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -107,3 +113,4 @@ var Nav = { } } }; +export default Nav; diff --git a/src/Miscellaneous/NormalizeURL.js b/src/Miscellaneous/NormalizeURL.js index 9b4a2bb9f8..eeaf6f2aec 100644 --- a/src/Miscellaneous/NormalizeURL.js +++ b/src/Miscellaneous/NormalizeURL.js @@ -1,3 +1,5 @@ +import { Conf, g } from "../globals/globals"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -25,3 +27,4 @@ const NormalizeURL = { } } }; +export default NormalizeURL; diff --git a/src/Miscellaneous/PSA.js b/src/Miscellaneous/PSA.js index 649760dacc..099c99e28a 100644 --- a/src/Miscellaneous/PSA.js +++ b/src/Miscellaneous/PSA.js @@ -1,3 +1,8 @@ +import Notice from "../classes/Notice"; +import { g, Conf, doc } from "../globals/globals"; +import Main from "../main/Main"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -21,3 +26,4 @@ const PSA = { } } }; +export default PSA; diff --git a/src/Miscellaneous/PSAHiding.js b/src/Miscellaneous/PSAHiding.js index e1a6066c0f..7a0bacceb9 100644 --- a/src/Miscellaneous/PSAHiding.js +++ b/src/Miscellaneous/PSAHiding.js @@ -1,3 +1,6 @@ +import Header from "../General/Header"; +import { Conf, doc, g } from "../globals/globals"; +import $ from "../platform/$"; /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -81,7 +84,7 @@ var PSAHiding = { } else { $.add(psa, [...Array.from(content.childNodes)]); } - // TODO check if hr exists - return PSAHiding.hr.hidden = psa.hidden; + if (PSAHiding.hr) PSAHiding.hr.hidden = psa.hidden; } }; +export default PSAHiding; diff --git a/src/Miscellaneous/PassMessage.js b/src/Miscellaneous/PassMessage.js index dd699a5ce4..8deae7ffbd 100644 --- a/src/Miscellaneous/PassMessage.js +++ b/src/Miscellaneous/PassMessage.js @@ -1,9 +1,11 @@ +import { Conf, d } from "../globals/globals"; +import $ from "../platform/$"; +import PassMessagePage from './PassMessage/PassMessageHtml'; /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -import PassMessagePage from './PassMessage/PassMessage.html'; const PassMessage = { init() { @@ -11,7 +13,7 @@ const PassMessage = { const msg = $.el('div', {className: 'box-outer top-box'} , - { innerHTML: PassMessagePage }); + PassMessagePage); msg.style.cssText = 'padding-bottom: 0;'; const close = $('a', msg); $.on(close, 'click', function() { @@ -28,3 +30,4 @@ const PassMessage = { }); } }; +export default PassMessage; diff --git a/src/Miscellaneous/PassMessage/PassMessage.html b/src/Miscellaneous/PassMessage/PassMessage.html deleted file mode 100644 index 2c1b02b620..0000000000 --- a/src/Miscellaneous/PassMessage/PassMessage.html +++ /dev/null @@ -1,11 +0,0 @@ -
-
-

- Trouble buying a 4chan Pass? (a message from 4chan X) - × -

-
-
- Check the 4chan X wiki for alternative solutions. -
-
diff --git a/src/Miscellaneous/PassMessage/PassMessageHtml.tsx b/src/Miscellaneous/PassMessage/PassMessageHtml.tsx new file mode 100644 index 0000000000..596bd6de62 --- /dev/null +++ b/src/Miscellaneous/PassMessage/PassMessageHtml.tsx @@ -0,0 +1,15 @@ +import h from '../../globals/jsx'; +import meta from '../../../package.json'; + +const passMessagePage =
+
+

+ Trouble buying a 4chan Pass? (a message from 4chan X) + × +

+
+
+ Check the 4chan X wiki for alternative solutions. +
+
; +export default passMessagePage; diff --git a/src/Miscellaneous/PostJumper.js b/src/Miscellaneous/PostJumper.js index 6f1ce1c195..1bed985011 100644 --- a/src/Miscellaneous/PostJumper.js +++ b/src/Miscellaneous/PostJumper.js @@ -1,3 +1,8 @@ +import Callbacks from "../classes/Callbacks"; +import { Conf, g, E } from "../globals/globals"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -87,3 +92,4 @@ var PostJumper = { return window.scrollBy(0, destPos-prevPos); } }; +export default PostJumper; diff --git a/src/Miscellaneous/RelativeDates.js b/src/Miscellaneous/RelativeDates.js index fa4db38b73..7f8141be91 100644 --- a/src/Miscellaneous/RelativeDates.js +++ b/src/Miscellaneous/RelativeDates.js @@ -1,3 +1,10 @@ +import Callbacks from "../classes/Callbacks"; +import Post from "../classes/Post"; +import Index from "../General/Index"; +import { g, Conf, d, doc } from "../globals/globals"; +import $ from "../platform/$"; +import { DAY, HOUR, MINUTE, SECOND } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -5,7 +12,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ var RelativeDates = { - INTERVAL: $.MINUTE / 2, + INTERVAL: 30000, init() { if ( @@ -45,32 +52,32 @@ var RelativeDates = { relative(diff, now, date, abbrev) { let number; let unit = (() => { - if ((number = (diff / $.DAY)) >= 1) { + if ((number = (diff / DAY)) >= 1) { const years = now.getFullYear() - date.getFullYear(); - let months = now.getMonth() - date.getMonth(); + let months = now.getMonth() - date.getMonth(); const days = now.getDate() - date.getDate(); - if (years > 1) { - number = years - ((months < 0) || ((months === 0) && (days < 0))); - return 'year'; - } else if ((years === 1) && ((months > 0) || ((months === 0) && (days >= 0)))) { - number = years; - return 'year'; + if (years > 1) { + number = years - ((months < 0) || ((months === 0) && (days < 0))); + return 'year'; + } else if ((years === 1) && ((months > 0) || ((months === 0) && (days >= 0)))) { + number = years; + return 'year'; } else if ((months = months + (12*years)) > 1) { - number = months - (days < 0); - return 'month'; - } else if ((months === 1) && (days >= 0)) { - number = months; - return 'month'; - } else { - return 'day'; - } - } else if ((number = (diff / $.HOUR)) >= 1) { + number = months - (days < 0); + return 'month'; + } else if ((months === 1) && (days >= 0)) { + number = months; + return 'month'; + } else { + return 'day'; + } + } else if ((number = (diff / HOUR)) >= 1) { return 'hour'; - } else if ((number = (diff / $.MINUTE)) >= 1) { + } else if ((number = (diff / MINUTE)) >= 1) { return 'minute'; } else { // prevent "-1 seconds ago" - number = Math.max(0, diff) / $.SECOND; + number = Math.max(0, diff) / SECOND; return 'second'; } })(); @@ -145,14 +152,14 @@ var RelativeDates = { }, setOwnTimeout(diff, data) { - const delay = diff < $.MINUTE ? - $.SECOND - ((diff + ($.SECOND / 2)) % $.SECOND) - : diff < $.HOUR ? - $.MINUTE - ((diff + ($.MINUTE / 2)) % $.MINUTE) - : diff < $.DAY ? - $.HOUR - ((diff + ($.HOUR / 2)) % $.HOUR) + const delay = diff < MINUTE ? + SECOND - ((diff + (SECOND / 2)) % SECOND) + : diff < HOUR ? + MINUTE - ((diff + (MINUTE / 2)) % MINUTE) + : diff < DAY ? + HOUR - ((diff + (HOUR / 2)) % HOUR) : - $.DAY - ((diff + ($.DAY / 2)) % $.DAY); + DAY - ((diff + (DAY / 2)) % DAY); return setTimeout(RelativeDates.markStale, delay, data); }, @@ -163,3 +170,4 @@ var RelativeDates = { return RelativeDates.stale.push(data); } }; +export default RelativeDates; diff --git a/src/Miscellaneous/RemoveSpoilers.js b/src/Miscellaneous/RemoveSpoilers.js index dd405358bf..ee44b5b67e 100644 --- a/src/Miscellaneous/RemoveSpoilers.js +++ b/src/Miscellaneous/RemoveSpoilers.js @@ -1,3 +1,8 @@ +import Callbacks from "../classes/Callbacks"; +import { Conf, doc, g } from "../globals/globals"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -35,3 +40,4 @@ var RemoveSpoilers = { } } }; +export default RemoveSpoilers; diff --git a/src/Miscellaneous/Report.js b/src/Miscellaneous/Report.js index 866ce158a8..dbecd09f33 100644 --- a/src/Miscellaneous/Report.js +++ b/src/Miscellaneous/Report.js @@ -1,9 +1,15 @@ +import Redirect from "../Archive/Redirect"; +import $ from "../platform/$"; +import ReportPage from './Report/ArchiveReport.html'; +import CSS from "../css/CSS"; +import Captcha from "../Posting/Captcha"; +import { Conf, d, g } from "../globals/globals"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -import ReportPage from './ArchiveReport.html'; var Report = { init() { @@ -135,3 +141,4 @@ var Report = { } } }; +export default Report; diff --git a/src/Miscellaneous/ThreadLinks.js b/src/Miscellaneous/ThreadLinks.js index 231935ff72..8d6a3aafdf 100644 --- a/src/Miscellaneous/ThreadLinks.js +++ b/src/Miscellaneous/ThreadLinks.js @@ -1,3 +1,6 @@ +import Callbacks from "../classes/Callbacks"; +import { g, Conf } from "../globals/globals"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -30,3 +33,4 @@ var ThreadLinks = { return link.target = '_blank'; } }; +export default ThreadLinks; diff --git a/src/Miscellaneous/Time.js b/src/Miscellaneous/Time.js index ad888ce931..2348113de1 100644 --- a/src/Miscellaneous/Time.js +++ b/src/Miscellaneous/Time.js @@ -1,3 +1,7 @@ +import $ from "../platform/$"; +import Callbacks from "../classes/Callbacks"; +import { g, Conf } from "../globals/globals"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -96,3 +100,4 @@ var Time = { '%'() { return '%'; } } }; +export default Time; diff --git a/src/Miscellaneous/Tinyboard.js b/src/Miscellaneous/Tinyboard.js index 23d026a88a..ae6e48de3a 100644 --- a/src/Miscellaneous/Tinyboard.js +++ b/src/Miscellaneous/Tinyboard.js @@ -1,3 +1,7 @@ +import { g } from "../globals/globals"; +import Main from "../main/Main"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -35,3 +39,4 @@ const Tinyboard = { } } }; +export default Tinyboard; diff --git a/src/Monitoring/Favicon.js b/src/Monitoring/Favicon.js index 6f3bff31c5..61d1332241 100644 --- a/src/Monitoring/Favicon.js +++ b/src/Monitoring/Favicon.js @@ -1,9 +1,3 @@ -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ import ferongr_unreadDead from './Favicon/ferongr.unreadDead.png'; import ferongr_unreadDeadY from './Favicon/ferongr.unreadDeadY.png'; import ferongr_unreadSFW from './Favicon/ferongr.unreadSFW.png'; @@ -42,7 +36,15 @@ import Metro_unreadNSFW from './Favicon/Metro.unreadNSFW.png'; import Metro_unreadNSFWY from './Favicon/Metro.unreadNSFWY.png'; import dead from './Favicon/dead.gif'; import empty from './Favicon/empty.gif'; +import $ from '../platform/$'; +import { Conf, d } from '../globals/globals'; +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ var Favicon = { init() { @@ -145,7 +147,7 @@ var Favicon = { SFW: '//s.4cdn.org/image/favicon-ws.ico', NSFW: '//s.4cdn.org/image/favicon.ico', - // TODO - dead: 'data:image/gif;base64,<%= readBase64("dead.gif") %>', - logo: 'data:image/png;base64,<%= readBase64("/src/meta/icon128.png") %>' + dead: `data:image/gif;base64,${dead}`, + logo: `data:image/png;base64,${empty}`, }; +export default Favicon; diff --git a/src/Monitoring/MarkNewIPs.js b/src/Monitoring/MarkNewIPs.js index 3bc7462157..b11c3b9e6d 100644 --- a/src/Monitoring/MarkNewIPs.js +++ b/src/Monitoring/MarkNewIPs.js @@ -1,3 +1,7 @@ +import Callbacks from "../classes/Callbacks"; +import { g, Conf } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -61,3 +65,4 @@ var MarkNewIPs = { return $.addClass(post.nodes.root, 'old-ip'); } }; +export default MarkNewIPs; diff --git a/src/Monitoring/ReplyPruning.js b/src/Monitoring/ReplyPruning.js index d61e941902..d59ede035c 100644 --- a/src/Monitoring/ReplyPruning.js +++ b/src/Monitoring/ReplyPruning.js @@ -1,3 +1,10 @@ +import Callbacks from "../classes/Callbacks"; +import Header from "../General/Header"; +import UI from "../General/UI"; +import { g, Conf, E, d } from "../globals/globals"; +import $ from "../platform/$"; +import QuoteThreading from "../Quotelinks/QuoteThreading"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -167,3 +174,4 @@ var ReplyPruning = { } } }; +export default ReplyPruning; diff --git a/src/Monitoring/ThreadStats.js b/src/Monitoring/ThreadStats.js index f6b40a07c4..6faa307468 100644 --- a/src/Monitoring/ThreadStats.js +++ b/src/Monitoring/ThreadStats.js @@ -1,3 +1,10 @@ +import Callbacks from "../classes/Callbacks"; +import Header from "../General/Header"; +import UI from "../General/UI"; +import { g, Conf, E, doc, d } from "../globals/globals"; +import $ from "../platform/$"; +import { MINUTE, SECOND } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -110,7 +117,7 @@ var ThreadStats = { $.addClass(ThreadStats.pageCountEl, 'warning'); return; } - ThreadStats.timeout = setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE); + ThreadStats.timeout = setTimeout(ThreadStats.fetchPage, 2 * MINUTE); return $.whenModified( g.SITE.urls.threadsListJSON(ThreadStats.thread), 'ThreadStats', @@ -144,7 +151,7 @@ var ThreadStats = { if (thread.no === ThreadStats.thread.ID) { ThreadStats.pageCountEl.textContent = pageNum + 1; ThreadStats.pageCountEl.classList.toggle('warning', (i >= (nThreads - this.response[0].threads.length))); - ThreadStats.lastPageUpdate = new Date(thread.last_modified * $.SECOND); + ThreadStats.lastPageUpdate = new Date(thread.last_modified * SECOND); ThreadStats.retry(); return; } @@ -167,6 +174,7 @@ var ThreadStats = { (ThreadStats.thread.posts.get(ThreadStats.thread.lastPost).info.date <= ThreadStats.lastPageUpdate) ) { return; } clearTimeout(ThreadStats.timeout); - return ThreadStats.timeout = setTimeout(ThreadStats.fetchPage, 5 * $.SECOND); + return ThreadStats.timeout = setTimeout(ThreadStats.fetchPage, 5 * SECOND); } }; +export default ThreadStats; diff --git a/src/Monitoring/ThreadUpdater.js b/src/Monitoring/ThreadUpdater.js index f0e0f2c633..8d5e457c7c 100644 --- a/src/Monitoring/ThreadUpdater.js +++ b/src/Monitoring/ThreadUpdater.js @@ -1,3 +1,18 @@ +import Beep from './ThreadUpdater/beep.wav'; +import $ from "../platform/$"; +import Callbacks from '../classes/Callbacks'; +import Notice from '../classes/Notice'; +import Post from '../classes/Post'; +import Main from '../main/Main'; +import Config from '../config/Config'; +import Settings from '../General/Settings'; +import QuoteThreading from '../Quotelinks/QuoteThreading'; +import Unread from './Unread'; +import Header from '../General/Header'; +import { g, Conf, d, doc } from '../globals/globals'; +import UI from '../General/UI'; +import { MINUTE, SECOND } from '../platform/helpers'; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -5,7 +20,6 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -import Beep from './ThreadUpdater/beep.wav'; var ThreadUpdater = { init() { @@ -112,7 +126,7 @@ var ThreadUpdater = { http://freesound.org/people/pierrecartoons1979/sounds/90112/ cc-by-nc-3.0 */ - beep: 'data:audio/wav;base64,<%= readBase64("beep.wav") %>', + beep: `data:audio/wav;base64,${Beep}`, playBeep() { const {audio} = ThreadUpdater; @@ -231,7 +245,7 @@ var ThreadUpdater = { // Fetching your own posts after posting if (ThreadUpdater.postID && (ThreadUpdater.checkPostCount < 5)) { ThreadUpdater.set('timer', '...', 'loading'); - ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND); + ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.update, ++ThreadUpdater.checkPostCount * SECOND); return; } @@ -298,7 +312,7 @@ var ThreadUpdater = { g.SITE.urls.threadJSON({boardID: ThreadUpdater.thread.board.ID, threadID: ThreadUpdater.thread.ID}), 'ThreadUpdater', ThreadUpdater.cb.load, - {timeout: $.MINUTE} + { timeout: MINUTE } ); }, @@ -330,7 +344,7 @@ var ThreadUpdater = { // XXX Reject updates that falsely delete the last post. if ((postObjects[postObjects.length-1].no < lastPost) && - ((new Date(req.getResponseHeader('Last-Modified')) - thread.posts.get(lastPost).info.date) < (30 * $.SECOND))) { return; } + ((new Date(req.getResponseHeader('Last-Modified')) - thread.posts.get(lastPost).info.date) < (30 * SECOND))) { return; } g.SITE.Build.spoilerRange[board] = OP.custom_spoiler; thread.setStatus('Archived', !!OP.archived); @@ -448,3 +462,4 @@ var ThreadUpdater = { ); } }; +export default ThreadUpdater; diff --git a/src/Monitoring/ThreadWatcher.js b/src/Monitoring/ThreadWatcher.js index cd199aae72..3a60bba36e 100644 --- a/src/Monitoring/ThreadWatcher.js +++ b/src/Monitoring/ThreadWatcher.js @@ -1,3 +1,26 @@ +import ThreadWatcherPage from './ThreadWatcher/ThreadWatcher.html'; +import $ from "../platform/$"; +import Board from '../classes/Board'; +import Callbacks from '../classes/Callbacks'; +import DataBoard from '../classes/DataBoard'; +import Thread from '../classes/Thread'; +import Filter from '../Filtering/Filter'; +import Main from '../main/Main'; +import $$ from '../platform/$$'; +import Config from '../config/Config'; +import CrossOrigin from '../platform/CrossOrigin'; +import PostRedirect from '../Posting/PostRedirect'; +import QuoteYou from '../Quotelinks/QuoteYou'; +import Unread from './Unread'; +import UnreadIndex from './UnreadIndex'; +import Header from '../General/Header'; +import Index from '../General/Index'; +import { Conf, d, doc, g } from '../globals/globals'; +import Menu from '../Menu/Menu'; +import UI from '../General/UI'; +import Get from '../General/Get'; +import { dict, HOUR, MINUTE } from '../platform/helpers'; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -6,7 +29,6 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -import ThreadWatcherPage from './ThreadWatcher/ThreadWatcher.html'; var ThreadWatcher = { init() { @@ -275,7 +297,7 @@ var ThreadWatcher = { url, 'ThreadWatcher', onloadend, - {timeout: $.MINUTE, ajax} + { timeout: MINUTE, ajax } ); return ThreadWatcher.requests.push(req); }, @@ -299,7 +321,7 @@ var ThreadWatcher = { }, initLastModified() { - const lm = ($.lastModified['ThreadWatcher'] || ($.lastModified['ThreadWatcher'] = $.dict())); + const lm = ($.lastModified['ThreadWatcher'] || ($.lastModified['ThreadWatcher'] = dict())); for (var siteID in ThreadWatcher.dbLM.data) { var boards = ThreadWatcher.dbLM.data[siteID]; for (var boardID in boards.boards) { @@ -321,7 +343,7 @@ var ThreadWatcher = { clearTimeout(ThreadWatcher.timeout); if (!Conf['Auto Update Thread Watcher']) { return; } const {db} = ThreadWatcher; - const interval = Conf['Show Page'] || (ThreadWatcher.unreadEnabled && Conf['Show Unread Count']) ? 5 * $.MINUTE : 2 * $.HOUR; + const interval = Conf['Show Page'] || (ThreadWatcher.unreadEnabled && Conf['Show Unread Count']) ? 5 * MINUTE : 2 * HOUR; const now = Date.now(); if ((now - interval >= ((middle = db.data.lastChecked || 0)) || middle > now) && !d.hidden && !!d.hasFocus()) { ThreadWatcher.fetchAllStatus(interval); @@ -355,7 +377,7 @@ var ThreadWatcher = { let middle1; const {db} = ThreadWatcher; const now = Date.now(); - const deep = !(now - (2 * $.HOUR) < ((middle1 = db.data.lastChecked2 || 0)) && middle1 <= now); + const deep = !(now - (2 * HOUR) < ((middle1 = db.data.lastChecked2 || 0)) && middle1 <= now); const boards = ThreadWatcher.getAll(true); for (var board of boards) { ThreadWatcher.fetchBoard(board, deep); @@ -395,7 +417,7 @@ var ThreadWatcher = { const {siteID, boardID} = board[0]; const lmDate = this.getResponseHeader('Last-Modified'); ThreadWatcher.dbLM.extend({siteID, boardID, val: $.item(url, lmDate)}); - const threads = $.dict(); + const threads = dict(); let pageLength = 0; let nThreads = 0; let oldest = null; @@ -612,7 +634,7 @@ var ThreadWatcher = { }, setPrefixes(threads) { - const prefixes = $.dict(); + const prefixes = dict(); for (var {siteID} of threads) { if (siteID in prefixes) { continue; } var len = 0; @@ -751,7 +773,7 @@ var ThreadWatcher = { }, addRaw(boardID, threadID, data, cb) { - const oldData = ThreadWatcher.db.get({boardID, threadID, defaultValue: $.dict()}); + const oldData = ThreadWatcher.db.get({ boardID, threadID, defaultValue: dict() }); delete oldData.last; delete oldData.modified; $.extend(oldData, data); @@ -896,3 +918,4 @@ var ThreadWatcher = { } } }; +export default ThreadWatcher; diff --git a/src/Monitoring/Unread.js b/src/Monitoring/Unread.js index 83fa6ea83d..bd580336da 100644 --- a/src/Monitoring/Unread.js +++ b/src/Monitoring/Unread.js @@ -1,3 +1,15 @@ +import Callbacks from "../classes/Callbacks"; +import DataBoard from "../classes/DataBoard"; +import RandomAccessList from "../classes/RandomAccessList"; +import Get from "../General/Get"; +import Header from "../General/Header"; +import { g, Conf, d } from "../globals/globals"; +import $ from "../platform/$"; +import { debounce, SECOND } from "../platform/helpers"; +import QuoteYou from "../Quotelinks/QuoteYou"; +import Favicon from "./Favicon"; +import ThreadWatcher from "./ThreadWatcher"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -179,7 +191,7 @@ var Unread = { return window.focus(); }; return notif.onshow = () => setTimeout(() => notif.close() - , 7 * $.SECOND); + , 7 * SECOND); }, onUpdate() { @@ -200,7 +212,7 @@ var Unread = { return Unread.update(); }, - read: $.debounce(100, function(e) { + read: debounce(100, function(e) { // Update the lastReadPost when hidden posts are added to the thread. if (!Unread.posts.size && (Unread.readCount !== Unread.thread.posts.keys.length)) { Unread.saveLastReadPost(); @@ -232,7 +244,7 @@ var Unread = { } }, - saveLastReadPost: $.debounce(2 * $.SECOND, function() { + saveLastReadPost: debounce(2 * SECOND, function() { let ID; $.forceSync('Remember Last Read Post'); if (!Conf['Remember Last Read Post'] || !Unread.db) { return; } @@ -300,7 +312,7 @@ var Unread = { } }, - saveThreadWatcherCount: $.debounce(2 * $.SECOND, function() { + saveThreadWatcherCount: debounce(2 * SECOND, function() { $.forceSync('Remember Last Read Post'); if (Conf['Remember Last Read Post'] && (!Unread.thread.isDead || Unread.thread.isArchived)) { let posts; @@ -328,3 +340,4 @@ var Unread = { } }) }; +export default Unread; diff --git a/src/Monitoring/UnreadIndex.js b/src/Monitoring/UnreadIndex.js index b58ed27d29..4db9b100b0 100644 --- a/src/Monitoring/UnreadIndex.js +++ b/src/Monitoring/UnreadIndex.js @@ -1,3 +1,15 @@ +import Callbacks from "../classes/Callbacks"; +import DataBoard from "../classes/DataBoard"; +import Get from "../General/Get"; +import Header from "../General/Header"; +import Index from "../General/Index"; +import { g, Conf, d } from "../globals/globals"; +import ExpandThread from "../Miscellaneous/ExpandThread"; +import $ from "../platform/$"; +import { dict } from "../platform/helpers"; +import QuoteYou from "../Quotelinks/QuoteYou"; +import ThreadWatcher from "./ThreadWatcher"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -5,9 +17,9 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ var UnreadIndex = { - lastReadPost: $.dict(), - hr: $.dict(), - markReadLink: $.dict(), + lastReadPost: dict(), + hr: dict(), + markReadLink: dict(), init() { if ((g.VIEW !== 'index') || !Conf['Remember Last Read Post'] || !Conf['Unread Line in Index']) { return; } @@ -143,3 +155,4 @@ var UnreadIndex = { ); } }; +export default UnreadIndex; diff --git a/src/Posting/Captcha.cache.js b/src/Posting/Captcha.cache.js deleted file mode 100644 index 393f69ba18..0000000000 --- a/src/Posting/Captcha.cache.js +++ /dev/null @@ -1,155 +0,0 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -Captcha.cache = { - init() { - $.on(d, 'SaveCaptcha', e => { - return this.saveAPI(e.detail); - }); - return $.on(d, 'NoCaptcha', e => { - return this.noCaptcha(e.detail); - }); - }, - - captchas: [], - - getCount() { - return this.captchas.length; - }, - - neededRaw() { - return !( - this.haveCookie() || this.captchas.length || QR.req || this.submitCB - ) && ( - (QR.posts.length > 1) || Conf['Auto-load captcha'] || !QR.posts[0].isOnlyQuotes() || QR.posts[0].file - ); - }, - - needed() { - return this.neededRaw() && $.event('LoadCaptcha'); - }, - - prerequest() { - if (!Conf['Prerequest Captcha']) { return; } - // Post count temporarily off by 1 when called from QR.post.rm, QR.close, or QR.submit - return $.queueTask(() => { - if ( - !this.prerequested && - this.neededRaw() && - !$.event('LoadCaptcha') && - !QR.captcha.occupied() && - (QR.cooldown.seconds <= 60) && - (QR.selected === QR.posts[QR.posts.length - 1]) && - !QR.selected.isOnlyQuotes() - ) { - const isReply = (QR.selected.thread !== 'new'); - if (!$.event('RequestCaptcha', {isReply})) { - this.prerequested = true; - this.submitCB = captcha => { - if (captcha) { return this.save(captcha); } - }; - return this.updateCount(); - } - } - }); - }, - - haveCookie() { - return /\b_ct=/.test(d.cookie) && (QR.posts[0].thread !== 'new'); - }, - - getOne() { - let captcha; - delete this.prerequested; - this.clear(); - if (captcha = this.captchas.shift()) { - this.count(); - return captcha; - } else { - return null; - } - }, - - request(isReply) { - if (!this.submitCB) { - if ($.event('RequestCaptcha', {isReply})) { return; } - } - return cb => { - this.submitCB = cb; - return this.updateCount(); - }; - }, - - abort() { - if (this.submitCB) { - delete this.submitCB; - $.event('AbortCaptcha'); - return this.updateCount(); - } - }, - - saveAPI(captcha) { - let cb; - if (cb = this.submitCB) { - delete this.submitCB; - cb(captcha); - return this.updateCount(); - } else { - return this.save(captcha); - } - }, - - noCaptcha(detail) { - let cb; - if (cb = this.submitCB) { - if (!this.haveCookie() || detail?.error) { - QR.error(detail?.error || 'Failed to retrieve captcha.'); - QR.captcha.setup(d.activeElement === QR.nodes.status); - } - delete this.submitCB; - cb(); - return this.updateCount(); - } - }, - - save(captcha) { - let cb; - if (cb = this.submitCB) { - this.abort(); - cb(captcha); - return; - } - this.captchas.push(captcha); - this.captchas.sort((a, b) => a.timeout - b.timeout); - return this.count(); - }, - - clear() { - if (this.captchas.length) { - let i; - const now = Date.now(); - for (i = 0; i < this.captchas.length; i++) { - var captcha = this.captchas[i]; - if (captcha.timeout > now) { break; } - } - if (i) { - this.captchas = this.captchas.slice(i); - return this.count(); - } - } - }, - - count() { - clearTimeout(this.timer); - if (this.captchas.length) { - this.timer = setTimeout(this.clear.bind(this), this.captchas[0].timeout - Date.now()); - } - return this.updateCount(); - }, - - updateCount() { - return $.event('CaptchaCount', this.captchas.length); - } -}; diff --git a/src/Posting/Captcha.js b/src/Posting/Captcha.js index 50b0d7038a..2531134e88 100644 --- a/src/Posting/Captcha.js +++ b/src/Posting/Captcha.js @@ -1 +1,422 @@ -Captcha = {}; +import $ from "../platform/$"; +import CaptchaReplace from "./Captcha.replace"; +import CaptchaT from "./Captcha.t"; +import meta from '../../package.json'; +import Main from "../main/Main"; +import Keybinds from "../Miscellaneous/Keybinds"; +import $$ from "../platform/$$"; +import QR from "./QR"; +import { Conf, d } from "../globals/globals"; +import { MINUTE, SECOND } from "../platform/helpers"; + +const Captcha = { + Cache: { + init() { + $.on(d, 'SaveCaptcha', e => { + return this.saveAPI(e.detail); + }); + return $.on(d, 'NoCaptcha', e => { + return this.noCaptcha(e.detail); + }); + }, + + captchas: [], + + getCount() { + return this.captchas.length; + }, + + neededRaw() { + return !( + this.haveCookie() || this.captchas.length || QR.req || this.submitCB + ) && ( + (QR.posts.length > 1) || Conf['Auto-load captcha'] || !QR.posts[0].isOnlyQuotes() || QR.posts[0].file + ); + }, + + needed() { + return this.neededRaw() && $.event('LoadCaptcha'); + }, + + prerequest() { + if (!Conf['Prerequest Captcha']) { return; } + // Post count temporarily off by 1 when called from QR.post.rm, QR.close, or QR.submit + return $.queueTask(() => { + if ( + !this.prerequested && + this.neededRaw() && + !$.event('LoadCaptcha') && + !QR.captcha.occupied() && + (QR.cooldown.seconds <= 60) && + (QR.selected === QR.posts[QR.posts.length - 1]) && + !QR.selected.isOnlyQuotes() + ) { + const isReply = (QR.selected.thread !== 'new'); + if (!$.event('RequestCaptcha', { isReply })) { + this.prerequested = true; + this.submitCB = captcha => { + if (captcha) { return this.save(captcha); } + }; + return this.updateCount(); + } + } + }); + }, + + haveCookie() { + return /\b_ct=/.test(d.cookie) && (QR.posts[0].thread !== 'new'); + }, + + getOne() { + let captcha; + delete this.prerequested; + this.clear(); + if (captcha = this.captchas.shift()) { + this.count(); + return captcha; + } else { + return null; + } + }, + + request(isReply) { + if (!this.submitCB) { + if ($.event('RequestCaptcha', { isReply })) { return; } + } + return cb => { + this.submitCB = cb; + return this.updateCount(); + }; + }, + + abort() { + if (this.submitCB) { + delete this.submitCB; + $.event('AbortCaptcha'); + return this.updateCount(); + } + }, + + saveAPI(captcha) { + let cb; + if (cb = this.submitCB) { + delete this.submitCB; + cb(captcha); + return this.updateCount(); + } else { + return this.save(captcha); + } + }, + + noCaptcha(detail) { + let cb; + if (cb = this.submitCB) { + if (!this.haveCookie() || detail?.error) { + QR.error(detail?.error || 'Failed to retrieve captcha.'); + QR.captcha.setup(d.activeElement === QR.nodes.status); + } + delete this.submitCB; + cb(); + return this.updateCount(); + } + }, + + save(captcha) { + let cb; + if (cb = this.submitCB) { + this.abort(); + cb(captcha); + return; + } + this.captchas.push(captcha); + this.captchas.sort((a, b) => a.timeout - b.timeout); + return this.count(); + }, + + clear() { + if (this.captchas.length) { + let i; + const now = Date.now(); + for (i = 0; i < this.captchas.length; i++) { + var captcha = this.captchas[i]; + if (captcha.timeout > now) { break; } + } + if (i) { + this.captchas = this.captchas.slice(i); + return this.count(); + } + } + }, + + count() { + clearTimeout(this.timer); + if (this.captchas.length) { + this.timer = setTimeout(this.clear.bind(this), this.captchas[0].timeout - Date.now()); + } + return this.updateCount(); + }, + + updateCount() { + return $.event('CaptchaCount', this.captchas.length); + } + }, Replace: CaptchaReplace, t: CaptchaT, v2: { + lifetime: 2 * MINUTE, + + init() { + if (d.cookie.indexOf('pass_enabled=1') >= 0) { return; } + if (!(this.isEnabled = !!$('#g-recaptcha, #captcha-forced-noscript') || !$.id('postForm'))) { return; } + + if (this.noscript = Conf['Force Noscript Captcha'] || !Main.jsEnabled) { + $.addClass(QR.nodes.el, 'noscript-captcha'); + } + + Captcha.cache.init(); + $.on(d, 'CaptchaCount', this.count.bind(this)); + + const root = $.el('div', { className: 'captcha-root' }); + $.extend(root, { + innerHTML: + '
' + } + ); + const counter = $('.captcha-counter > a', root); + this.nodes = { root, counter }; + this.count(); + $.addClass(QR.nodes.el, 'has-captcha', 'captcha-v2'); + $.after(QR.nodes.com.parentNode, root); + + $.on(counter, 'click', this.toggle.bind(this)); + $.on(counter, 'keydown', e => { + if (Keybinds.keyCode(e) !== 'Space') { return; } + this.toggle(); + e.preventDefault(); + return e.stopPropagation(); + }); + return $.on(window, 'captcha:success', () => { + // XXX Greasemonkey 1.x workaround to gain access to GM_* functions. + return $.queueTask(() => this.save(false)); + }); + }, + + timeouts: {}, + prevNeeded: 0, + + noscriptURL() { + let lang; + let url = `https://www.google.com/recaptcha/api/fallback?k=${meta.recaptchaKey}`; + if (lang = Conf['captchaLanguage'].trim()) { + url += `&hl=${encodeURIComponent(lang)}`; + } + return url; + }, + + moreNeeded() { + // Post count temporarily off by 1 when called from QR.post.rm, QR.close, or QR.submit + return $.queueTask(() => { + const needed = Captcha.cache.needed(); + if (needed && !this.prevNeeded) { + this.setup(QR.cooldown.auto && (d.activeElement === QR.nodes.status)); + } + return this.prevNeeded = needed; + }); + }, + + toggle() { + if (this.nodes.container && !this.timeouts.destroy) { + return this.destroy(); + } else { + return this.setup(true, true); + } + }, + + setup(focus, force) { + if (!this.isEnabled || (!Captcha.cache.needed() && !force)) { return; } + + if (focus) { + $.addClass(QR.nodes.el, 'focus'); + this.nodes.counter.focus(); + } + + if (this.timeouts.destroy) { + clearTimeout(this.timeouts.destroy); + delete this.timeouts.destroy; + return this.reload(); + } + + if (this.nodes.container) { + // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=1226835 + $.queueTask(() => { + let iframe; + if (this.nodes.container && (d.activeElement === this.nodes.counter) && (iframe = $('iframe[src^="https://www.google.com/recaptcha/"]', this.nodes.container))) { + iframe.focus(); + return QR.focus(); + } + }); // Event handler not fired in Firefox + return; + } + + this.nodes.container = $.el('div', { className: 'captcha-container' }); + $.prepend(this.nodes.root, this.nodes.container); + new MutationObserver(this.afterSetup.bind(this)).observe(this.nodes.container, { + childList: true, + subtree: true + } + ); + + if (this.noscript) { + return this.setupNoscript(); + } else { + return this.setupJS(); + } + }, + + setupNoscript() { + const iframe = $.el('iframe', { + id: 'qr-captcha-iframe', + scrolling: 'no', + src: this.noscriptURL() + } + ); + const div = $.el('div'); + const textarea = $.el('textarea'); + $.add(div, textarea); + return $.add(this.nodes.container, [iframe, div]); + }, + + setupJS() { + return $.global(function () { + const render = function () { + const { classList } = document.documentElement; + const container = document.querySelector('#qr .captcha-container'); + return container.dataset.widgetID = window.grecaptcha.render(container, { + sitekey: meta.recaptchaKey, + theme: classList.contains('tomorrow') || classList.contains('spooky') || classList.contains('dark-captcha') ? 'dark' : 'light', + callback(response) { + return window.dispatchEvent(new CustomEvent('captcha:success', { detail: response })); + } + } + ); + }; + if (window.grecaptcha) { + return render(); + } else { + const cbNative = window.onRecaptchaLoaded; + window.onRecaptchaLoaded = function () { + render(); + return cbNative(); + }; + if (!document.head.querySelector('script[src^="https://www.google.com/recaptcha/api.js"]')) { + const script = document.createElement('script'); + script.src = 'https://www.google.com/recaptcha/api.js?onload=onRecaptchaLoaded&render=explicit'; + return document.head.appendChild(script); + } + } + }); + }, + + afterSetup(mutations) { + for (var mutation of mutations) { + for (var node of mutation.addedNodes) { + var iframe, textarea; + if (iframe = $.x('./descendant-or-self::iframe[starts-with(@src, "https://www.google.com/recaptcha/")]', node)) { this.setupIFrame(iframe); } + if (textarea = $.x('./descendant-or-self::textarea', node)) { this.setupTextArea(textarea); } + } + } + }, + + setupIFrame(iframe) { + let needle; + if (!doc.contains(iframe)) { return; } + Captcha.replace.iframe(iframe); + $.addClass(QR.nodes.el, 'captcha-open'); + this.fixQRPosition(); + $.on(iframe, 'load', this.fixQRPosition); + if (d.activeElement === this.nodes.counter) { iframe.focus(); } + // XXX Make sure scroll on space prevention (see src/css/style.css) doesn't cause scrolling of div + if (['blink', 'edge'].includes($.engine) && (needle = iframe.parentNode, $$('#qr .captcha-container > div > div:first-of-type').includes(needle))) { + return $.on(iframe.parentNode, 'scroll', function () { return this.scrollTop = 0; }); + } + }, + + fixQRPosition() { + if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { + QR.nodes.el.style.top = ''; + return QR.nodes.el.style.bottom = '0px'; + } + }, + + setupTextArea(textarea) { + return $.one(textarea, 'input', () => this.save(true)); + }, + + destroy() { + if (!this.isEnabled) { return; } + delete this.timeouts.destroy; + $.rmClass(QR.nodes.el, 'captcha-open'); + if (this.nodes.container) { + $.global(function () { + const container = document.querySelector('#qr .captcha-container'); + return window.grecaptcha.reset(container.dataset.widgetID); + }); + $.rm(this.nodes.container); + return delete this.nodes.container; + } + }, + + getOne(isReply) { + return Captcha.cache.getOne(isReply); + }, + + save(pasted, token) { + Captcha.cache.save({ + response: token || $('textarea', this.nodes.container).value, + timeout: Date.now() + this.lifetime + }); + + const focus = (d.activeElement?.nodeName === 'IFRAME') && /https?:\/\/www\.google\.com\/recaptcha\//.test(d.activeElement.src); + if (Captcha.cache.needed()) { + if (focus) { + if (QR.cooldown.auto || Conf['Post on Captcha Completion']) { + this.nodes.counter.focus(); + } else { + QR.nodes.status.focus(); + } + } + this.reload(); + } else { + if (pasted) { + this.destroy(); + } else { + if (this.timeouts.destroy == null) { this.timeouts.destroy = setTimeout(this.destroy.bind(this), 3 * SECOND); } + } + if (focus) { QR.nodes.status.focus(); } + } + + if (Conf['Post on Captcha Completion'] && !QR.cooldown.auto) { return QR.submit(); } + }, + + count() { + const count = Captcha.cache.getCount(); + const loading = Captcha.cache.submitCB ? '...' : ''; + this.nodes.counter.textContent = `Captchas: ${count}${loading}`; + return this.moreNeeded(); + }, + + reload() { + if ($('iframe[src^="https://www.google.com/recaptcha/api/fallback?"]', this.nodes.container)) { + this.destroy(); + return this.setup(false, true); + } else { + return $.global(function () { + const container = document.querySelector('#qr .captcha-container'); + return window.grecaptcha.reset(container.dataset.widgetID); + }); + } + }, + + occupied() { + return !!this.nodes.container && !this.timeouts.destroy; + } + } +}; +export default Captcha; diff --git a/src/Posting/Captcha.replace.js b/src/Posting/Captcha.replace.js index 80bdcb8fb1..ad553e05cb 100644 --- a/src/Posting/Captcha.replace.js +++ b/src/Posting/Captcha.replace.js @@ -1,9 +1,13 @@ +import { g, Conf, doc } from "../globals/globals"; +import Main from "../main/Main"; +import $ from "../platform/$"; +import Captcha from "./Captcha"; /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -Captcha.replace = { +const CaptchaReplace = { init() { if ((g.SITE.software !== 'yotsuba') || (d.cookie.indexOf('pass_enabled=1') >= 0)) { return; } @@ -50,3 +54,4 @@ Captcha.replace = { } } }; +export default CaptchaReplace; diff --git a/src/Posting/Captcha.t.js b/src/Posting/Captcha.t.js index f61d7886d7..724a6d7e1b 100644 --- a/src/Posting/Captcha.t.js +++ b/src/Posting/Captcha.t.js @@ -1,9 +1,13 @@ +import { d, g } from "../globals/globals"; +import $ from "../platform/$"; +import QR from "./QR"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -Captcha.t = { +const CaptchaT = { init() { if (d.cookie.indexOf('pass_enabled=1') >= 0) { return; } if (!(this.isEnabled = !!$('#t-root') || !$.id('postForm'))) { return; } @@ -35,7 +39,7 @@ Captcha.t = { if (!this.nodes.container) { this.nodes.container = $.el('div', {className: 'captcha-container'}); $.prepend(this.nodes.root, this.nodes.container); - Captcha.t.currentThread = Captcha.t.getThread(); + CaptchaT.currentThread = CaptchaT.getThread(); $.global(function() { const el = document.querySelector('#qr .captcha-container'); window.TCaptcha.init(el, this.boardID, +this.threadID); @@ -45,7 +49,7 @@ Captcha.t = { }}) )); } - , Captcha.t.currentThread); + , CaptchaT.currentThread); } if (focus) { @@ -62,11 +66,11 @@ Captcha.t = { updateThread() { if (!this.isEnabled) { return; } - const {boardID, threadID} = (Captcha.t.currentThread || {}); - const newThread = Captcha.t.getThread(); + const {boardID, threadID} = (CaptchaT.currentThread || {}); + const newThread = CaptchaT.getThread(); if ((newThread.boardID !== boardID) || (newThread.threadID !== threadID)) { - Captcha.t.destroy(); - return Captcha.t.setup(); + CaptchaT.destroy(); + return CaptchaT.setup(); } }, @@ -95,3 +99,4 @@ Captcha.t = { return !!this.nodes.container; } }; +export default CaptchaT; diff --git a/src/Posting/Captcha.v2.js b/src/Posting/Captcha.v2.js deleted file mode 100644 index 1addcfd9cf..0000000000 --- a/src/Posting/Captcha.v2.js +++ /dev/null @@ -1,266 +0,0 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * DS104: Avoid inline assignments - * DS204: Change includes calls to have a more natural evaluation order - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -Captcha.v2 = { - lifetime: 2 * $.MINUTE, - - init() { - if (d.cookie.indexOf('pass_enabled=1') >= 0) { return; } - if (!(this.isEnabled = !!$('#g-recaptcha, #captcha-forced-noscript') || !$.id('postForm'))) { return; } - - if (this.noscript = Conf['Force Noscript Captcha'] || !Main.jsEnabled) { - $.addClass(QR.nodes.el, 'noscript-captcha'); - } - - Captcha.cache.init(); - $.on(d, 'CaptchaCount', this.count.bind(this)); - - const root = $.el('div', {className: 'captcha-root'}); - $.extend(root, { innerHTML: - '
' - } - ); - const counter = $('.captcha-counter > a', root); - this.nodes = {root, counter}; - this.count(); - $.addClass(QR.nodes.el, 'has-captcha', 'captcha-v2'); - $.after(QR.nodes.com.parentNode, root); - - $.on(counter, 'click', this.toggle.bind(this)); - $.on(counter, 'keydown', e => { - if (Keybinds.keyCode(e) !== 'Space') { return; } - this.toggle(); - e.preventDefault(); - return e.stopPropagation(); - }); - return $.on(window, 'captcha:success', () => { - // XXX Greasemonkey 1.x workaround to gain access to GM_* functions. - return $.queueTask(() => this.save(false)); - }); - }, - - timeouts: {}, - prevNeeded: 0, - - noscriptURL() { - let lang; - let url = 'https://www.google.com/recaptcha/api/fallback?k=<%= meta.recaptchaKey %>'; - if (lang = Conf['captchaLanguage'].trim()) { - url += `&hl=${encodeURIComponent(lang)}`; - } - return url; - }, - - moreNeeded() { - // Post count temporarily off by 1 when called from QR.post.rm, QR.close, or QR.submit - return $.queueTask(() => { - const needed = Captcha.cache.needed(); - if (needed && !this.prevNeeded) { - this.setup(QR.cooldown.auto && (d.activeElement === QR.nodes.status)); - } - return this.prevNeeded = needed; - }); - }, - - toggle() { - if (this.nodes.container && !this.timeouts.destroy) { - return this.destroy(); - } else { - return this.setup(true, true); - } - }, - - setup(focus, force) { - if (!this.isEnabled || (!Captcha.cache.needed() && !force)) { return; } - - if (focus) { - $.addClass(QR.nodes.el, 'focus'); - this.nodes.counter.focus(); - } - - if (this.timeouts.destroy) { - clearTimeout(this.timeouts.destroy); - delete this.timeouts.destroy; - return this.reload(); - } - - if (this.nodes.container) { - // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=1226835 - $.queueTask(() => { - let iframe; - if (this.nodes.container && (d.activeElement === this.nodes.counter) && (iframe = $('iframe[src^="https://www.google.com/recaptcha/"]', this.nodes.container))) { - iframe.focus(); - return QR.focus(); - } - }); // Event handler not fired in Firefox - return; - } - - this.nodes.container = $.el('div', {className: 'captcha-container'}); - $.prepend(this.nodes.root, this.nodes.container); - new MutationObserver(this.afterSetup.bind(this)).observe(this.nodes.container, { - childList: true, - subtree: true - } - ); - - if (this.noscript) { - return this.setupNoscript(); - } else { - return this.setupJS(); - } - }, - - setupNoscript() { - const iframe = $.el('iframe', { - id: 'qr-captcha-iframe', - scrolling: 'no', - src: this.noscriptURL() - } - ); - const div = $.el('div'); - const textarea = $.el('textarea'); - $.add(div, textarea); - return $.add(this.nodes.container, [iframe, div]); - }, - - setupJS() { - return $.global(function() { - const render = function() { - const {classList} = document.documentElement; - const container = document.querySelector('#qr .captcha-container'); - return container.dataset.widgetID = window.grecaptcha.render(container, { - sitekey: '<%= meta.recaptchaKey %>', - theme: classList.contains('tomorrow') || classList.contains('spooky') || classList.contains('dark-captcha') ? 'dark' : 'light', - callback(response) { - return window.dispatchEvent(new CustomEvent('captcha:success', {detail: response})); - } - } - ); - }; - if (window.grecaptcha) { - return render(); - } else { - const cbNative = window.onRecaptchaLoaded; - window.onRecaptchaLoaded = function() { - render(); - return cbNative(); - }; - if (!document.head.querySelector('script[src^="https://www.google.com/recaptcha/api.js"]')) { - const script = document.createElement('script'); - script.src = 'https://www.google.com/recaptcha/api.js?onload=onRecaptchaLoaded&render=explicit'; - return document.head.appendChild(script); - } - } - }); - }, - - afterSetup(mutations) { - for (var mutation of mutations) { - for (var node of mutation.addedNodes) { - var iframe, textarea; - if (iframe = $.x('./descendant-or-self::iframe[starts-with(@src, "https://www.google.com/recaptcha/")]', node)) { this.setupIFrame(iframe); } - if (textarea = $.x('./descendant-or-self::textarea', node)) { this.setupTextArea(textarea); } - } - } - }, - - setupIFrame(iframe) { - let needle; - if (!doc.contains(iframe)) { return; } - Captcha.replace.iframe(iframe); - $.addClass(QR.nodes.el, 'captcha-open'); - this.fixQRPosition(); - $.on(iframe, 'load', this.fixQRPosition); - if (d.activeElement === this.nodes.counter) { iframe.focus(); } - // XXX Make sure scroll on space prevention (see src/css/style.css) doesn't cause scrolling of div - if (['blink', 'edge'].includes($.engine) && (needle = iframe.parentNode, $$('#qr .captcha-container > div > div:first-of-type').includes(needle))) { - return $.on(iframe.parentNode, 'scroll', function() { return this.scrollTop = 0; }); - } - }, - - fixQRPosition() { - if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { - QR.nodes.el.style.top = ''; - return QR.nodes.el.style.bottom = '0px'; - } - }, - - setupTextArea(textarea) { - return $.one(textarea, 'input', () => this.save(true)); - }, - - destroy() { - if (!this.isEnabled) { return; } - delete this.timeouts.destroy; - $.rmClass(QR.nodes.el, 'captcha-open'); - if (this.nodes.container) { - $.global(function() { - const container = document.querySelector('#qr .captcha-container'); - return window.grecaptcha.reset(container.dataset.widgetID); - }); - $.rm(this.nodes.container); - return delete this.nodes.container; - } - }, - - getOne(isReply) { - return Captcha.cache.getOne(isReply); - }, - - save(pasted, token) { - Captcha.cache.save({ - response: token || $('textarea', this.nodes.container).value, - timeout: Date.now() + this.lifetime - }); - - const focus = (d.activeElement?.nodeName === 'IFRAME') && /https?:\/\/www\.google\.com\/recaptcha\//.test(d.activeElement.src); - if (Captcha.cache.needed()) { - if (focus) { - if (QR.cooldown.auto || Conf['Post on Captcha Completion']) { - this.nodes.counter.focus(); - } else { - QR.nodes.status.focus(); - } - } - this.reload(); - } else { - if (pasted) { - this.destroy(); - } else { - if (this.timeouts.destroy == null) { this.timeouts.destroy = setTimeout(this.destroy.bind(this), 3 * $.SECOND); } - } - if (focus) { QR.nodes.status.focus(); } - } - - if (Conf['Post on Captcha Completion'] && !QR.cooldown.auto) { return QR.submit(); } - }, - - count() { - const count = Captcha.cache.getCount(); - const loading = Captcha.cache.submitCB ? '...' : ''; - this.nodes.counter.textContent = `Captchas: ${count}${loading}`; - return this.moreNeeded(); - }, - - reload() { - if ($('iframe[src^="https://www.google.com/recaptcha/api/fallback?"]', this.nodes.container)) { - this.destroy(); - return this.setup(false, true); - } else { - return $.global(function() { - const container = document.querySelector('#qr .captcha-container'); - return window.grecaptcha.reset(container.dataset.widgetID); - }); - } - }, - - occupied() { - return !!this.nodes.container && !this.timeouts.destroy; - } -}; diff --git a/src/Posting/PassLink.js b/src/Posting/PassLink.js index bc5432358e..68016f64a5 100644 --- a/src/Posting/PassLink.js +++ b/src/Posting/PassLink.js @@ -1,3 +1,6 @@ +import { g, Conf } from "../globals/globals"; +import Main from "../main/Main"; +import $ from "../platform/$"; /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -22,3 +25,4 @@ const PassLink = { return $.before(styleSelector.previousSibling, [passLink, $.tn('\u00A0\u00A0')]); } }; +export default PassLink; diff --git a/src/Posting/PostRedirect.js b/src/Posting/PostRedirect.js index f0a042f6d2..d61526b953 100644 --- a/src/Posting/PostRedirect.js +++ b/src/Posting/PostRedirect.js @@ -1,3 +1,6 @@ +import { d } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -32,3 +35,4 @@ const PostRedirect = { }; } }; +export default PostRedirect; diff --git a/src/Posting/PostSuccessful.js b/src/Posting/PostSuccessful.js index 7dcb9a91ba..86623b88c9 100644 --- a/src/Posting/PostSuccessful.js +++ b/src/Posting/PostSuccessful.js @@ -1,3 +1,7 @@ +import DataBoard from "../classes/DataBoard"; +import { Conf, d, g } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -26,3 +30,4 @@ const PostSuccessful = { }); } }; +export default PostSuccessful; diff --git a/src/Posting/QR.cooldown.js b/src/Posting/QR.cooldown.js deleted file mode 100644 index 48e15dcbca..0000000000 --- a/src/Posting/QR.cooldown.js +++ /dev/null @@ -1,246 +0,0 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -QR.cooldown = { - seconds: 0, - delays: { - deletion: 60 - }, // cooldown for deleting posts/files - - // Called from Main - init() { - if (!Conf['Quick Reply']) { return; } - this.data = Conf['cooldowns']; - this.changes = $.dict(); - return $.sync('cooldowns', this.sync); - }, - - // Called from QR - setup() { - // Read cooldown times - $.extend(QR.cooldown.delays, g.BOARD.cooldowns()); - - // The longest reply cooldown, for use in pruning old reply data - QR.cooldown.maxDelay = 0; - for (var type in QR.cooldown.delays) { - var delay = QR.cooldown.delays[type]; - if (!['thread', 'thread_global'].includes(type)) { - QR.cooldown.maxDelay = Math.max(QR.cooldown.maxDelay, delay); - } - } - - QR.cooldown.isSetup = true; - return QR.cooldown.start(); - }, - - start() { - const {data} = QR.cooldown; - if ( - !Conf['Cooldown'] || - !QR.cooldown.isSetup || - !!QR.cooldown.isCounting || - ((Object.keys(data[g.BOARD.ID] || {}).length + Object.keys(data.global || {}).length) <= 0) - ) { return; } - QR.cooldown.isCounting = true; - return QR.cooldown.count(); - }, - - sync(data) { - QR.cooldown.data = data || $.dict(); - return QR.cooldown.start(); - }, - - add(threadID, postID) { - if (!Conf['Cooldown']) { return; } - const start = Date.now(); - const boardID = g.BOARD.ID; - QR.cooldown.set(boardID, start, {threadID, postID}); - if (threadID === postID) { QR.cooldown.set('global', start, {boardID, threadID, postID}); } - QR.cooldown.save(); - return QR.cooldown.start(); - }, - - addDelay(post, delay) { - if (!Conf['Cooldown']) { return; } - const cooldown = QR.cooldown.categorize(post); - cooldown.delay = delay; - QR.cooldown.set(g.BOARD.ID, Date.now(), cooldown); - QR.cooldown.save(); - return QR.cooldown.start(); - }, - - addMute(delay) { - if (!Conf['Cooldown']) { return; } - QR.cooldown.set(g.BOARD.ID, Date.now(), {type: 'mute', delay}); - QR.cooldown.save(); - return QR.cooldown.start(); - }, - - delete(post) { - let cooldown; - if (!QR.cooldown.data) { return; } - const cooldowns = (QR.cooldown.data[post.board.ID] || (QR.cooldown.data[post.board.ID] = $.dict())); - for (var id in cooldowns) { - cooldown = cooldowns[id]; - if ((cooldown.delay == null) && (cooldown.threadID === post.thread.ID) && (cooldown.postID === post.ID)) { - QR.cooldown.set(post.board.ID, id, null); - } - } - return QR.cooldown.save(); - }, - - secondsDeletion(post) { - if (!QR.cooldown.data || !Conf['Cooldown']) { return 0; } - const cooldowns = QR.cooldown.data[post.board.ID] || $.dict(); - for (var start in cooldowns) { - var cooldown = cooldowns[start]; - if ((cooldown.delay == null) && (cooldown.threadID === post.thread.ID) && (cooldown.postID === post.ID)) { - var seconds = QR.cooldown.delays.deletion - Math.floor((Date.now() - start) / $.SECOND); - return Math.max(seconds, 0); - } - } - return 0; - }, - - categorize(post) { - if (post.thread === 'new') { - return {type: 'thread'}; - } else { - return { - type: !!post.file ? 'image' : 'reply', - threadID: +post.thread - }; - } - }, - - mergeChange(data, scope, id, value) { - if (value) { - return (data[scope] || (data[scope] = $.dict()))[id] = value; - } else if (scope in data) { - delete data[scope][id]; - if (Object.keys(data[scope]).length === 0) { return delete data[scope]; } - } - }, - - set(scope, id, value) { - QR.cooldown.mergeChange(QR.cooldown.data, scope, id, value); - return (QR.cooldown.changes[scope] || (QR.cooldown.changes[scope] = $.dict()))[id] = value; - }, - - save() { - const {changes} = QR.cooldown; - if (!Object.keys(changes).length) { return; } - return $.get('cooldowns', $.dict(), function({cooldowns}) { - for (var scope in QR.cooldown.changes) { - for (var id in QR.cooldown.changes[scope]) { - var value = QR.cooldown.changes[scope][id]; - QR.cooldown.mergeChange(cooldowns, scope, id, value); - } - QR.cooldown.data = cooldowns; - } - return $.set('cooldowns', cooldowns, () => QR.cooldown.changes = $.dict()); - }); - }, - - clear() { - QR.cooldown.data = $.dict(); - QR.cooldown.changes = $.dict(); - QR.cooldown.auto = false; - QR.cooldown.update(); - return $.queueTask($.delete, 'cooldowns'); - }, - - update() { - let cooldown; - if (!QR.cooldown.isCounting) { return; } - - let save = false; - let nCooldowns = 0; - const now = Date.now(); - const {type, threadID} = QR.cooldown.categorize(QR.posts[0]); - let seconds = 0; - - if (Conf['Cooldown']) { for (var scope of [g.BOARD.ID, 'global']) { - var cooldowns = (QR.cooldown.data[scope] || (QR.cooldown.data[scope] = $.dict())); - - for (var start in cooldowns) { - cooldown = cooldowns[start]; - start = +start; - var elapsed = Math.floor((now - start) / $.SECOND); - if (elapsed < 0) { // clock changed since then? - QR.cooldown.set(scope, start, null); - save = true; - continue; - } - - // Explicit delays from error messages - if (cooldown.delay != null) { - if (cooldown.delay <= elapsed) { - QR.cooldown.set(scope, start, null); - save = true; - } else if (((cooldown.type === type) && (cooldown.threadID === threadID)) || (cooldown.type === 'mute')) { - // Delays only apply to the given post type and thread. - seconds = Math.max(seconds, cooldown.delay - elapsed); - } - continue; - } - - // Clean up expired cooldowns - var maxDelay = cooldown.threadID !== cooldown.postID ? - QR.cooldown.maxDelay - : - QR.cooldown.delays[scope === 'global' ? 'thread_global' : 'thread']; - if (QR.cooldown.customCooldown) { - maxDelay = Math.max(maxDelay, parseInt(Conf['customCooldown'], 10)); - } - if (maxDelay <= elapsed) { - QR.cooldown.set(scope, start, null); - save = true; - continue; - } - - if (((type === 'thread') === (cooldown.threadID === cooldown.postID)) && (cooldown.boardID !== g.BOARD.ID)) { - // Only cooldowns relevant to this post can set the seconds variable: - // reply cooldown with a reply, thread cooldown with a thread. - // Inter-board thread cooldowns only apply on boards other than the one they were posted on. - var suffix = scope === 'global' ? - '_global' - : - ''; - seconds = Math.max(seconds, QR.cooldown.delays[type + suffix] - elapsed); - - // If additional cooldown is enabled, add the configured seconds to the count. - if (QR.cooldown.customCooldown) { - seconds = Math.max(seconds, parseInt(Conf['customCooldown'], 10) - elapsed); - } - } - } - - nCooldowns += Object.keys(cooldowns).length; - } } - - if (save) { QR.cooldown.save; } - - if (nCooldowns) { - clearTimeout(QR.cooldown.timeout); - QR.cooldown.timeout = setTimeout(QR.cooldown.count, $.SECOND); - } else { - delete QR.cooldown.isCounting; - } - - // Update the status when we change posting type. - // Don't get stuck at some random number. - // Don't interfere with progress status updates. - const update = seconds !== QR.cooldown.seconds; - QR.cooldown.seconds = seconds; - if (update) { return QR.status(); } - }, - - count() { - QR.cooldown.update(); - if ((QR.cooldown.seconds === 0) && QR.cooldown.auto && !QR.req) { return QR.submit(); } - } -}; diff --git a/src/Posting/QR.js b/src/Posting/QR.js index d3015a2de9..d74c479a39 100644 --- a/src/Posting/QR.js +++ b/src/Posting/QR.js @@ -1,3 +1,21 @@ +import QuickReplyPage from './QR/QuickReply.html'; +import $ from '../platform/$'; +import Callbacks from '../classes/Callbacks'; +import Notice from '../classes/Notice'; +import Main from '../main/Main'; +import Favicon from '../Monitoring/Favicon'; +import $$ from '../platform/$$'; +import CrossOrigin from '../platform/CrossOrigin'; +import Captcha from './Captcha'; +import meta from '../../package.json'; +import Header from '../General/Header'; +import { Conf, E, d, doc, g } from '../globals/globals'; +import Menu from '../Menu/Menu'; +import UI from '../General/UI'; +import BoardConfig from '../General/BoardConfig'; +import Get from '../General/Get'; +import { DAY, dict, SECOND } from '../platform/helpers'; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -6,7 +24,6 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -import QuickReplyPage from './QR/QuickReply.html'; var QR = { mimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'application/vnd.adobe.flash.movie', 'application/x-shockwave-flash', 'video/webm'], @@ -92,7 +109,10 @@ var QR = { if (origToggle = $.id('togglePostFormLink')) { const link = $.el('h1', {className: "qr-link-container"}); - $.extend(link, {innerHTML: '?{g.VIEW === "thread"}{Reply to Thread}{Start a Thread}'}); + $.extend(link, { + innerHTML: + `${g.VIEW === "thread" ? "Reply to Thread" : "Start a Thread"}` + }); QR.link = link.firstElementChild; $.on(link.firstChild, 'click', function() { @@ -294,7 +314,7 @@ var QR = { notif.onclose = null; return notif.close(); } - , 7 * $.SECOND); + , 7 * SECOND); } } }, @@ -452,7 +472,10 @@ var QR = { openError() { const div = $.el('div'); - $.extend(div, { innerHTML: 'Could not open file. [More info]'}); + $.extend(div, { + innerHTML: + 'Could not open file. [More info]' + }); return QR.error(div); }, @@ -947,8 +970,8 @@ var QR = { post.unlock(); if (err = this.response?.getElementById('errmsg')) { // error! - // TODO: check if exists - $('a', err).target = '_blank'; // duplicate image link + const el = $('a', err); + if (el) el.target = '_blank'; // duplicate image link } else if (connErr = (!this.response || (this.response.title !== 'Post successful!'))) { err = QR.connectionError(); if ((QR.captcha === Captcha.v2) && QR.currentCaptcha) { Captcha.cache.save(QR.currentCaptcha); } @@ -1078,7 +1101,7 @@ var QR = { if ((attempts >= 6) || (this.status === 200)) { return cb(); } else { - return setTimeout(check, attempts * $.SECOND); + return setTimeout(check, attempts * SECOND); } }, responseType: 'text', @@ -1101,5 +1124,1028 @@ var QR = { QR.notifications.push(new Notice('info', 'QR upload aborted.', 5)); } return QR.status(); + }, + + Cooldown: { + seconds: 0, + delays: { + deletion: 60 + }, // cooldown for deleting posts/files + + // Called from Main + init() { + if (!Conf['Quick Reply']) { return; } + this.data = Conf['cooldowns']; + this.changes = dict(); + return $.sync('cooldowns', this.sync); + }, + + // Called from QR + setup() { + // Read cooldown times + $.extend(QR.cooldown.delays, g.BOARD.cooldowns()); + + // The longest reply cooldown, for use in pruning old reply data + QR.cooldown.maxDelay = 0; + for (var type in QR.cooldown.delays) { + var delay = QR.cooldown.delays[type]; + if (!['thread', 'thread_global'].includes(type)) { + QR.cooldown.maxDelay = Math.max(QR.cooldown.maxDelay, delay); + } + } + + QR.cooldown.isSetup = true; + return QR.cooldown.start(); + }, + + start() { + const { data } = QR.cooldown; + if ( + !Conf['Cooldown'] || + !QR.cooldown.isSetup || + !!QR.cooldown.isCounting || + ((Object.keys(data[g.BOARD.ID] || {}).length + Object.keys(data.global || {}).length) <= 0) + ) { return; } + QR.cooldown.isCounting = true; + return QR.cooldown.count(); + }, + + sync(data) { + QR.cooldown.data = data || dict(); + return QR.cooldown.start(); + }, + + add(threadID, postID) { + if (!Conf['Cooldown']) { return; } + const start = Date.now(); + const boardID = g.BOARD.ID; + QR.cooldown.set(boardID, start, { threadID, postID }); + if (threadID === postID) { QR.cooldown.set('global', start, { boardID, threadID, postID }); } + QR.cooldown.save(); + return QR.cooldown.start(); + }, + + addDelay(post, delay) { + if (!Conf['Cooldown']) { return; } + const cooldown = QR.cooldown.categorize(post); + cooldown.delay = delay; + QR.cooldown.set(g.BOARD.ID, Date.now(), cooldown); + QR.cooldown.save(); + return QR.cooldown.start(); + }, + + addMute(delay) { + if (!Conf['Cooldown']) { return; } + QR.cooldown.set(g.BOARD.ID, Date.now(), { type: 'mute', delay }); + QR.cooldown.save(); + return QR.cooldown.start(); + }, + + delete(post) { + let cooldown; + if (!QR.cooldown.data) { return; } + const cooldowns = (QR.cooldown.data[post.board.ID] || (QR.cooldown.data[post.board.ID] = dict())); + for (var id in cooldowns) { + cooldown = cooldowns[id]; + if ((cooldown.delay == null) && (cooldown.threadID === post.thread.ID) && (cooldown.postID === post.ID)) { + QR.cooldown.set(post.board.ID, id, null); + } + } + return QR.cooldown.save(); + }, + + secondsDeletion(post) { + if (!QR.cooldown.data || !Conf['Cooldown']) { return 0; } + const cooldowns = QR.cooldown.data[post.board.ID] || dict(); + for (var start in cooldowns) { + var cooldown = cooldowns[start]; + if ((cooldown.delay == null) && (cooldown.threadID === post.thread.ID) && (cooldown.postID === post.ID)) { + var seconds = QR.cooldown.delays.deletion - Math.floor((Date.now() - start) / SECOND); + return Math.max(seconds, 0); + } + } + return 0; + }, + + categorize(post) { + if (post.thread === 'new') { + return { type: 'thread' }; + } else { + return { + type: !!post.file ? 'image' : 'reply', + threadID: +post.thread + }; + } + }, + + mergeChange(data, scope, id, value) { + if (value) { + return (data[scope] || (data[scope] = dict()))[id] = value; + } else if (scope in data) { + delete data[scope][id]; + if (Object.keys(data[scope]).length === 0) { return delete data[scope]; } + } + }, + + set(scope, id, value) { + QR.cooldown.mergeChange(QR.cooldown.data, scope, id, value); + return (QR.cooldown.changes[scope] || (QR.cooldown.changes[scope] = dict()))[id] = value; + }, + + save() { + const { changes } = QR.cooldown; + if (!Object.keys(changes).length) { return; } + return $.get('cooldowns', dict(), function ({ cooldowns }) { + for (var scope in QR.cooldown.changes) { + for (var id in QR.cooldown.changes[scope]) { + var value = QR.cooldown.changes[scope][id]; + QR.cooldown.mergeChange(cooldowns, scope, id, value); + } + QR.cooldown.data = cooldowns; + } + return $.set('cooldowns', cooldowns, () => QR.cooldown.changes = dict()); + }); + }, + + clear() { + QR.cooldown.data = dict(); + QR.cooldown.changes = dict(); + QR.cooldown.auto = false; + QR.cooldown.update(); + return $.queueTask($.delete, 'cooldowns'); + }, + + update() { + let cooldown; + if (!QR.cooldown.isCounting) { return; } + + let save = false; + let nCooldowns = 0; + const now = Date.now(); + const { type, threadID } = QR.cooldown.categorize(QR.posts[0]); + let seconds = 0; + + if (Conf['Cooldown']) { + for (var scope of [g.BOARD.ID, 'global']) { + var cooldowns = (QR.cooldown.data[scope] || (QR.cooldown.data[scope] = dict())); + + for (var start in cooldowns) { + cooldown = cooldowns[start]; + start = +start; + var elapsed = Math.floor((now - start) / SECOND); + if (elapsed < 0) { // clock changed since then? + QR.cooldown.set(scope, start, null); + save = true; + continue; + } + + // Explicit delays from error messages + if (cooldown.delay != null) { + if (cooldown.delay <= elapsed) { + QR.cooldown.set(scope, start, null); + save = true; + } else if (((cooldown.type === type) && (cooldown.threadID === threadID)) || (cooldown.type === 'mute')) { + // Delays only apply to the given post type and thread. + seconds = Math.max(seconds, cooldown.delay - elapsed); + } + continue; + } + + // Clean up expired cooldowns + var maxDelay = cooldown.threadID !== cooldown.postID ? + QR.cooldown.maxDelay + : + QR.cooldown.delays[scope === 'global' ? 'thread_global' : 'thread']; + if (QR.cooldown.customCooldown) { + maxDelay = Math.max(maxDelay, parseInt(Conf['customCooldown'], 10)); + } + if (maxDelay <= elapsed) { + QR.cooldown.set(scope, start, null); + save = true; + continue; + } + + if (((type === 'thread') === (cooldown.threadID === cooldown.postID)) && (cooldown.boardID !== g.BOARD.ID)) { + // Only cooldowns relevant to this post can set the seconds variable: + // reply cooldown with a reply, thread cooldown with a thread. + // Inter-board thread cooldowns only apply on boards other than the one they were posted on. + var suffix = scope === 'global' ? + '_global' + : + ''; + seconds = Math.max(seconds, QR.cooldown.delays[type + suffix] - elapsed); + + // If additional cooldown is enabled, add the configured seconds to the count. + if (QR.cooldown.customCooldown) { + seconds = Math.max(seconds, parseInt(Conf['customCooldown'], 10) - elapsed); + } + } + } + + nCooldowns += Object.keys(cooldowns).length; + } + } + + if (save) { QR.cooldown.save; } + + if (nCooldowns) { + clearTimeout(QR.cooldown.timeout); + QR.cooldown.timeout = setTimeout(QR.cooldown.count, SECOND); + } else { + delete QR.cooldown.isCounting; + } + + // Update the status when we change posting type. + // Don't get stuck at some random number. + // Don't interfere with progress status updates. + const update = seconds !== QR.cooldown.seconds; + QR.cooldown.seconds = seconds; + if (update) { return QR.status(); } + }, + + count() { + QR.cooldown.update(); + if ((QR.cooldown.seconds === 0) && QR.cooldown.auto && !QR.req) { return QR.submit(); } + } + }, + + oekaki: { + menu: { + init() { + if (!['index', 'thread'].includes(g.VIEW) || !Conf['Menu'] || !Conf['Edit Link'] || !Conf['Quick Reply']) { return; } + + const a = $.el('a', { + className: 'edit-link', + href: 'javascript:;', + textContent: 'Edit image' + } + ); + $.on(a, 'click', this.editFile); + + return Menu.menu.addEntry({ + el: a, + order: 90, + open(post) { + QR.oekaki.menu.post = post; + const { file } = post; + return QR.postingIsEnabled && !!file && (file.isImage || file.isVideo); + } + }); + }, + + editFile() { + const { post } = QR.oekaki.menu; + QR.quote.call(post.nodes.post); + const { isVideo } = post.file; + const currentTime = post.file.fullImage?.currentTime || 0; + return CrossOrigin.file(post.file.url, function (blob) { + if (!blob) { + return QR.error("Can't load file."); + } else if (isVideo) { + const video = $.el('video'); + $.on(video, 'loadedmetadata', function () { + $.on(video, 'seeked', function () { + const canvas = $.el('canvas', { + width: video.videoWidth, + height: video.videoHeight + } + ); + canvas.getContext('2d').drawImage(video, 0, 0); + return canvas.toBlob(function (snapshot) { + snapshot.name = post.file.name.replace(/\.\w+$/, '') + '.png'; + QR.handleFiles([snapshot]); + return QR.oekaki.edit(); + }); + }); + return video.currentTime = currentTime; + }); + $.on(video, 'error', () => QR.openError()); + return video.src = URL.createObjectURL(blob); + } else { + blob.name = post.file.name; + QR.handleFiles([blob]); + return QR.oekaki.edit(); + } + }); + } + }, + + setup() { + return $.global(function () { + const { FCX } = window; + FCX.oekakiCB = () => window.Tegaki.flatten().toBlob(function (file) { + const source = `oekaki-${Date.now()}`; + FCX.oekakiLatest = source; + return document.dispatchEvent(new CustomEvent('QRSetFile', { + bubbles: true, + detail: { file, name: FCX.oekakiName, source } + })); + }); + if (window.Tegaki) { + return document.querySelector('#qr .oekaki').hidden = false; + } + }); + }, + + load(cb) { + if ($('script[src^="//s.4cdn.org/js/tegaki"]', d.head)) { + return cb(); + } else { + const style = $.el('link', { + rel: 'stylesheet', + href: `//s.4cdn.org/css/tegaki.${Date.now()}.css` + } + ); + const script = $.el('script', + { src: `//s.4cdn.org/js/tegaki.min.${Date.now()}.js` }); + let n = 0; + const onload = function () { + if (++n === 2) { return cb(); } + }; + $.on(style, 'load', onload); + $.on(script, 'load', onload); + return $.add(d.head, [style, script]); + } + }, + + draw() { + return $.global(function () { + const { Tegaki, FCX } = window; + if (Tegaki.bg) { Tegaki.destroy(); } + FCX.oekakiName = 'tegaki.png'; + return Tegaki.open({ + onDone: FCX.oekakiCB, + onCancel() { return Tegaki.bgColor = '#ffffff'; }, + width: +document.querySelector('#qr [name=oekaki-width]').value, + height: +document.querySelector('#qr [name=oekaki-height]').value, + bgColor: + document.querySelector('#qr [name=oekaki-bg]').checked ? + document.querySelector('#qr [name=oekaki-bgcolor]').value + : + 'transparent' + }); + }); + }, + + button() { + if (QR.selected.file) { + return QR.oekaki.edit(); + } else { + return QR.oekaki.toggle(); + } + }, + + edit() { + return QR.oekaki.load(() => $.global(function () { + const { Tegaki, FCX } = window; + const name = document.getElementById('qr-filename').value.replace(/\.\w+$/, '') + '.png'; + const { source } = document.getElementById('file-n-submit').dataset; + const error = content => document.dispatchEvent(new CustomEvent('CreateNotification', { + bubbles: true, + detail: { type: 'warning', content, lifetime: 20 } + })); + var cb = function (e) { + if (e) { this.removeEventListener('QRMetadata', cb, false); } + const selected = document.getElementById('selected'); + if (!selected?.dataset.type) { return error('No file to edit.'); } + if (!/^(image|video)\//.test(selected.dataset.type)) { return error('Not an image.'); } + if (!selected.dataset.height) { return error('Metadata not available.'); } + if (selected.dataset.height === 'loading') { + selected.addEventListener('QRMetadata', cb, false); + return; + } + if (Tegaki.bg) { Tegaki.destroy(); } + FCX.oekakiName = name; + Tegaki.open({ + onDone: FCX.oekakiCB, + onCancel() { return Tegaki.bgColor = '#ffffff'; }, + width: +selected.dataset.width, + height: +selected.dataset.height, + bgColor: 'transparent' + }); + const canvas = document.createElement('canvas'); + canvas.width = (canvas.naturalWidth = +selected.dataset.width); + canvas.height = (canvas.naturalHeight = +selected.dataset.height); + canvas.hidden = true; + document.body.appendChild(canvas); + canvas.addEventListener('QRImageDrawn', function () { + this.remove(); + return Tegaki.onOpenImageLoaded.call(this); + } + , false); + return canvas.dispatchEvent(new CustomEvent('QRDrawFile', { bubbles: true })); + }; + if (Tegaki.bg && (Tegaki.onDoneCb === FCX.oekakiCB) && (source === FCX.oekakiLatest)) { + FCX.oekakiName = name; + return Tegaki.resume(); + } else { + return cb(); + } + })); + }, + + toggle() { + return QR.oekaki.load(() => QR.nodes.oekaki.hidden = !QR.nodes.oekaki.hidden); + } + }, + + persona: { + always: {}, + types: { + name: [], + email: [], + sub: [] + }, + + init() { + if (!Conf['Quick Reply'] && (!Conf['Menu'] || !Conf['Delete Link'])) { return; } + for (var item of Conf['QR.personas'].split('\n')) { + QR.persona.parseItem(item.trim()); + } + }, + + parseItem(item) { + let match, needle, type, val; + if (item[0] === '#') { return; } + if (!(match = item.match(/(name|options|email|subject|password):"(.*)"/i))) { return; } + [match, type, val] = Array.from(match); + + // Don't mix up item settings with val. + item = item.replace(match, ''); + + const boards = item.match(/boards:([^;]+)/i)?.[1].toLowerCase() || 'global'; + if ((boards !== 'global') && (needle = g.BOARD.ID, !boards.split(',').includes(needle))) { return; } + + + if (type === 'password') { + QR.persona.pwd = val; + return; + } + + if (type === 'options') { type = 'email'; } + if (type === 'subject') { type = 'sub'; } + + if (/always/i.test(item)) { + QR.persona.always[type] = val; + } + + if (!QR.persona.types[type].includes(val)) { + return QR.persona.types[type].push(val); + } + }, + + load() { + for (var type in QR.persona.types) { + var arr = QR.persona.types[type]; + var list = $(`#list-${type}`, QR.nodes.el); + for (var val of arr) { + if (val) { + $.add(list, $.el('option', + { textContent: val }) + ); + } + } + } + }, + + getPassword() { + let m; + if (QR.persona.pwd != null) { + return QR.persona.pwd; + } else if (m = d.cookie.match(/4chan_pass=([^;]+)/)) { + return decodeURIComponent(m[1]); + } else { + return ''; + } + }, + + get(cb) { + return $.get('QR.persona', {}, ({ 'QR.persona': persona }) => cb(persona)); + }, + + set(post) { + return $.get('QR.persona', {}, function ({ 'QR.persona': persona }) { + persona = { + name: post.name, + flag: post.flag + }; + return $.set('QR.persona', persona); + }); + } + }, + + post: class { + constructor(select) { + this.select = this.select.bind(this); + const el = $.el('a', { + className: 'qr-preview', + draggable: true, + href: 'javascript:;' + } + ); + $.extend(el, { innerHTML: '' }); + + this.nodes = { + el, + rm: el.firstChild, + spoiler: $('.qr-preview-spoiler input', el), + span: el.lastChild + }; + + $.on(el, 'click', this.select); + $.on(this.nodes.rm, 'click', e => { e.stopPropagation(); return this.rm(); }); + $.on(this.nodes.spoiler, 'change', e => { + this.spoiler = e.target.checked; + if (this === QR.selected) { QR.nodes.spoiler.checked = this.spoiler; } + return this.preventAutoPost(); + }); + for (var label of $$('label', el)) { + $.on(label, 'click', e => e.stopPropagation()); + } + $.add(QR.nodes.dumpList, el); + + for (var event of ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop']) { + $.on(el, event.toLowerCase(), this[event]); + } + + this.thread = g.VIEW === 'thread' ? + g.THREADID + : + 'new'; + + const prev = QR.posts[QR.posts.length - 1]; + QR.posts.push(this); + this.nodes.spoiler.checked = (this.spoiler = prev && Conf['Remember Spoiler'] ? + prev.spoiler + : + false); + QR.persona.get(persona => { + this.name = 'name' in QR.persona.always ? + QR.persona.always.name + : prev ? + prev.name + : + persona.name; + + this.email = 'email' in QR.persona.always ? + QR.persona.always.email + : + ''; + + this.sub = 'sub' in QR.persona.always ? + QR.persona.always.sub + : + ''; + + if (QR.nodes.flag) { + this.flag = (() => { + if (prev) { + return prev.flag; + } else if (persona.flag && persona.flag in g.BOARD.config.board_flags) { + return persona.flag; + } + })(); + } + if (QR.selected === this) { return this.load(); } + }); // load persona + if (select) { this.select(); } + this.unlock(); + QR.captcha.moreNeeded(); + } + + rm() { + this.delete(); + const index = QR.posts.indexOf(this); + if (QR.posts.length === 1) { + new QR.post(true); + $.rmClass(QR.nodes.el, 'dump'); + } else if (this === QR.selected) { + (QR.posts[index - 1] || QR.posts[index + 1]).select(); + } + QR.posts.splice(index, 1); + QR.status(); + return QR.captcha.updateThread?.(); + } + + delete() { + $.rm(this.nodes.el); + URL.revokeObjectURL(this.URL); + return this.dismissErrors(); + } + + lock(lock = true) { + this.isLocked = lock; + if (this !== QR.selected) { return; } + for (var name of ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag']) { + var node; + if ((node = QR.nodes[name])) { + node.disabled = lock; + } + } + this.nodes.rm.style.visibility = lock ? 'hidden' : ''; + this.nodes.spoiler.disabled = lock; + return this.nodes.el.draggable = !lock; + } + + unlock() { + return this.lock(false); + } + + select() { + if (QR.selected) { + QR.selected.nodes.el.removeAttribute('id'); + QR.selected.forceSave(); + } + QR.selected = this; + this.lock(this.isLocked); + this.nodes.el.id = 'selected'; + // Scroll the list to center the focused post. + const rectEl = this.nodes.el.getBoundingClientRect(); + const rectList = this.nodes.el.parentNode.getBoundingClientRect(); + this.nodes.el.parentNode.scrollLeft += (rectEl.left + (rectEl.width / 2)) - rectList.left - (rectList.width / 2); + return this.load(); + } + + load() { + // Load this post's values. + + for (var name of ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag']) { + var node; + if (!(node = QR.nodes[name])) { continue; } + node.value = this[name] || node.dataset.default || ''; + } + + (this.thread !== 'new' ? $.addClass : $.rmClass)(QR.nodes.el, 'reply-to-thread'); + + this.showFileData(); + return QR.characterCount(); + } + + save(input, forced) { + if (input.type === 'checkbox') { + this.spoiler = input.checked; + return; + } + const { name } = input.dataset; + if (!['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag'].includes(name)) { return; } + const prev = this[name] || input.dataset.default || null; + this[name] = input.value || input.dataset.default || null; + switch (name) { + case 'thread': + (this.thread !== 'new' ? $.addClass : $.rmClass)(QR.nodes.el, 'reply-to-thread'); + QR.status(); + QR.captcha.updateThread?.(); + break; + case 'com': + this.updateComment(); + break; + case 'filename': + if (!this.file) { return; } + this.saveFilename(); + this.updateFilename(); + break; + case 'name': case 'flag': + if (this[name] !== prev) { // only save manual changes, not values filled in by persona settings + QR.persona.set(this); + } + break; + } + if (!forced) { return this.preventAutoPost(); } + } + + forceSave() { + if (this !== QR.selected) { return; } + // Do this in case people use extensions + // that do not trigger the `input` event. + for (var name of ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler', 'flag']) { + var node; + if (!(node = QR.nodes[name])) { continue; } + this.save(node, true); + } + } + + preventAutoPost() { + // Disable auto-posting if you're editing the first post + // during the last 5 seconds of the cooldown. + if (QR.cooldown.auto && (this === QR.posts[0])) { + QR.cooldown.update(); // adding/removing file can change cooldown + if (QR.cooldown.seconds <= 5) { return QR.cooldown.auto = false; } + } + } + + setComment(com) { + this.com = com || null; + if (this === QR.selected) { + QR.nodes.com.value = this.com; + } + return this.updateComment(); + } + + updateComment() { + if (this === QR.selected) { + QR.characterCount(); + } + this.nodes.span.textContent = this.com; + QR.captcha.moreNeeded(); + if (QR.captcha === Captcha.v2) { + return Captcha.cache.prerequest(); + } + } + + isOnlyQuotes() { + return (this.com || '').trim() === (this.quotedText || '').trim(); + } + + static rmErrored(e) { + e.stopPropagation(); + for (let i = QR.posts.length - 1; i >= 0; i--) { + var errors; + var post = QR.posts[i]; + if ((errors = post.errors)) { + for (var error of errors) { + if (doc.contains(error)) { + post.rm(); + break; + } + } + } + } + } + + error(className, message, link) { + const div = $.el('div', { className }); + $.extend(div, { + innerHTML: message + (link ? ` [More info]` : '') + + `
[delete post] [delete all]` + }); + (this.errors || (this.errors = [])).push(div); + const [rm, rmAll] = Array.from($$('a', div)); + $.on(div, 'click', () => { + if (QR.posts.includes(this)) { return this.select(); } + }); + $.on(rm, 'click', e => { + e.stopPropagation(); + if (QR.posts.includes(this)) { return this.rm(); } + }); + $.on(rmAll, 'click', QR.post.rmErrored); + return QR.error(div, true); + } + + fileError(message, link) { + return this.error('file-error', `${this.filename}: ${message}`, link); + } + + dismissErrors(test = () => true) { + if (this.errors) { + for (var error of this.errors) { + if (doc.contains(error) && test(error)) { + error.parentNode.previousElementSibling.click(); + } + } + } + } + + setFile(file) { + this.file = file; + if (Conf['Randomize Filename'] && (g.BOARD.ID !== 'f')) { + let ext; + this.filename = `${Date.now() - Math.floor(Math.random() * 365 * DAY)}`; + if (ext = this.file.name.match(QR.validExtension)) { this.filename += ext[0]; } + } else { + this.filename = this.file.name; + } + this.filesize = $.bytesToString(this.file.size); + this.checkSize(); + $.addClass(this.nodes.el, 'has-file'); + QR.captcha.moreNeeded(); + URL.revokeObjectURL(this.URL); + this.saveFilename(); + if (this === QR.selected) { + this.showFileData(); + } else { + this.updateFilename(); + } + this.rmMetadata(); + this.nodes.el.dataset.type = this.file.type; + this.nodes.el.style.backgroundImage = ''; + if (!QR.mimeTypes.includes(this.file.type)) { + this.fileError('Unsupported file type.'); + } else if (/^(image|video)\//.test(this.file.type)) { + this.readFile(); + } + return this.preventAutoPost(); + } + + checkSize() { + let max = QR.max_size; + if (/^video\//.test(this.file.type)) { max = Math.min(max, QR.max_size_video); } + if (this.file.size > max) { + return this.fileError(`File too large (file: ${this.filesize}, max: ${$.bytesToString(max)}).`); + } + } + + readFile() { + const isVideo = /^video\//.test(this.file.type); + const el = $.el(isVideo ? 'video' : 'img'); + if (isVideo && !el.canPlayType(this.file.type)) { return; } + + const event = isVideo ? 'loadeddata' : 'load'; + var onload = () => { + $.off(el, event, onload); + $.off(el, 'error', onerror); + this.checkDimensions(el); + this.setThumbnail(el); + return $.event('QRMetadata', null, this.nodes.el); + }; + var onerror = () => { + $.off(el, event, onload); + $.off(el, 'error', onerror); + this.fileError(`Corrupt ${isVideo ? 'video' : 'image'} or error reading metadata.`, meta.faq + '#error-reading-metadata'); + URL.revokeObjectURL(el.src); + // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=1021289 + this.nodes.el.removeAttribute('data-height'); + return $.event('QRMetadata', null, this.nodes.el); + }; + this.nodes.el.dataset.height = 'loading'; + $.on(el, event, onload); + $.on(el, 'error', onerror); + return el.src = URL.createObjectURL(this.file); + } + + checkDimensions(el) { + let height, width; + if (el.tagName === 'IMG') { + ({ height, width } = el); + this.nodes.el.dataset.height = height; + this.nodes.el.dataset.width = width; + if ((height > QR.max_height) || (width > QR.max_width)) { + this.fileError(`Image too large (image: ${height}x${width}px, max: ${QR.max_height}x${QR.max_width}px)`); + } + if ((height < QR.min_height) || (width < QR.min_width)) { + return this.fileError(`Image too small (image: ${height}x${width}px, min: ${QR.min_height}x${QR.min_width}px)`); + } + } else { + const { videoHeight, videoWidth, duration } = el; + this.nodes.el.dataset.height = videoHeight; + this.nodes.el.dataset.width = videoWidth; + this.nodes.el.dataset.duration = duration; + const max_height = Math.min(QR.max_height, QR.max_height_video); + const max_width = Math.min(QR.max_width, QR.max_width_video); + if ((videoHeight > max_height) || (videoWidth > max_width)) { + this.fileError(`Video too large (video: ${videoHeight}x${videoWidth}px, max: ${max_height}x${max_width}px)`); + } + if ((videoHeight < QR.min_height) || (videoWidth < QR.min_width)) { + this.fileError(`Video too small (video: ${videoHeight}x${videoWidth}px, min: ${QR.min_height}x${QR.min_width}px)`); + } + if (!isFinite(duration)) { + this.fileError('Video lacks duration metadata (try remuxing)'); + } else if (duration > QR.max_duration_video) { + this.fileError(`Video too long (video: ${duration}s, max: ${QR.max_duration_video}s)`); + } + if (BoardConfig.noAudio(g.BOARD.ID) && $.hasAudio(el)) { + return this.fileError('Audio not allowed'); + } + } + } + + setThumbnail(el) { + // Create a redimensioned thumbnail. + let height, width; + const isVideo = el.tagName === 'VIDEO'; + + // Generate thumbnails only if they're really big. + // Resized pictures through canvases look like ass, + // so we generate thumbnails `s` times bigger then expected + // to avoid crappy resized quality. + let s = 90 * 2 * window.devicePixelRatio; + if (this.file.type === 'image/gif') { s *= 3; } // let them animate + if (isVideo) { + height = el.videoHeight; + width = el.videoWidth; + } else { + ({ height, width } = el); + if ((height < s) || (width < s)) { + this.URL = el.src; + this.nodes.el.style.backgroundImage = `url(${this.URL})`; + return; + } + } + + if (height <= width) { + width = (s / height) * width; + height = s; + } else { + height = (s / width) * height; + width = s; + } + const cv = $.el('canvas'); + cv.height = height; + cv.width = width; + cv.getContext('2d').drawImage(el, 0, 0, width, height); + URL.revokeObjectURL(el.src); + return cv.toBlob(blob => { + this.URL = URL.createObjectURL(blob); + return this.nodes.el.style.backgroundImage = `url(${this.URL})`; + }); + } + + rmFile() { + if (this.isLocked) { return; } + delete this.file; + delete this.filename; + delete this.filesize; + this.nodes.el.removeAttribute('title'); + QR.nodes.filename.removeAttribute('title'); + this.rmMetadata(); + this.nodes.el.style.backgroundImage = ''; + $.rmClass(this.nodes.el, 'has-file'); + this.showFileData(); + URL.revokeObjectURL(this.URL); + this.dismissErrors(error => $.hasClass(error, 'file-error')); + return this.preventAutoPost(); + } + + rmMetadata() { + for (var attr of ['type', 'height', 'width', 'duration']) { + // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=1021289 + this.nodes.el.removeAttribute(`data-${attr}`); + } + } + + saveFilename() { + this.file.newName = (this.filename || '').replace(/[/\\]/g, '-'); + if (!QR.validExtension.test(this.filename)) { + // 4chan will truncate the filename if it has no extension. + return this.file.newName += `.${$.getOwn(QR.extensionFromType, this.file.type) || 'jpg'}`; + } + } + + updateFilename() { + const long = `${this.filename} (${this.filesize})`; + this.nodes.el.title = long; + if (this !== QR.selected) { return; } + return QR.nodes.filename.title = long; + } + + showFileData() { + if (this.file) { + this.updateFilename(); + QR.nodes.filename.value = this.filename; + $.addClass(QR.nodes.oekaki, 'has-file'); + $.addClass(QR.nodes.fileSubmit, 'has-file'); + } else { + $.rmClass(QR.nodes.oekaki, 'has-file'); + $.rmClass(QR.nodes.fileSubmit, 'has-file'); + } + if (this.file?.source != null) { + QR.nodes.fileSubmit.dataset.source = this.file.source; + } else { + QR.nodes.fileSubmit.removeAttribute('data-source'); + } + return QR.nodes.spoiler.checked = this.spoiler; + } + + pasteText(file) { + this.pasting = true; + this.preventAutoPost(); + const reader = new FileReader(); + reader.onload = e => { + const { result } = e.target; + this.setComment((this.com ? `${this.com}\n${result}` : result)); + return delete this.pasting; + }; + return reader.readAsText(file); + } + + dragStart(e) { + const { left, top } = this.getBoundingClientRect(); + e.dataTransfer.setDragImage(this, e.clientX - left, e.clientY - top); + return $.addClass(this, 'drag'); + } + dragEnd() { return $.rmClass(this, 'drag'); } + dragEnter() { return $.addClass(this, 'over'); } + dragLeave() { return $.rmClass(this, 'over'); } + + dragOver(e) { + e.preventDefault(); + return e.dataTransfer.dropEffect = 'move'; + } + + drop() { + $.rmClass(this, 'over'); + if (!this.draggable) { return; } + const el = $('.drag', this.parentNode); + const index = el => [...Array.from(el.parentNode.children)].indexOf(el); + const oldIndex = index(el); + const newIndex = index(this); + if (QR.posts[oldIndex].isLocked || QR.posts[newIndex].isLocked) { return; } + (oldIndex < newIndex ? $.after : $.before)(this, el); + const post = QR.posts.splice(oldIndex, 1)[0]; + QR.posts.splice(newIndex, 0, post); + QR.status(); + return QR.captcha.updateThread?.(); + } } + }; +export default QR; diff --git a/src/Posting/QR.oekaki.js b/src/Posting/QR.oekaki.js deleted file mode 100644 index e1342fa394..0000000000 --- a/src/Posting/QR.oekaki.js +++ /dev/null @@ -1,183 +0,0 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -QR.oekaki = { - menu: { - init() { - if (!['index', 'thread'].includes(g.VIEW) || !Conf['Menu'] || !Conf['Edit Link'] || !Conf['Quick Reply']) { return; } - - const a = $.el('a', { - className: 'edit-link', - href: 'javascript:;', - textContent: 'Edit image' - } - ); - $.on(a, 'click', this.editFile); - - return Menu.menu.addEntry({ - el: a, - order: 90, - open(post) { - QR.oekaki.menu.post = post; - const {file} = post; - return QR.postingIsEnabled && !!file && (file.isImage || file.isVideo); - } - }); - }, - - editFile() { - const {post} = QR.oekaki.menu; - QR.quote.call(post.nodes.post); - const {isVideo} = post.file; - const currentTime = post.file.fullImage?.currentTime || 0; - return CrossOrigin.file(post.file.url, function(blob) { - if (!blob) { - return QR.error("Can't load file."); - } else if (isVideo) { - const video = $.el('video'); - $.on(video, 'loadedmetadata', function() { - $.on(video, 'seeked', function() { - const canvas = $.el('canvas', { - width: video.videoWidth, - height: video.videoHeight - } - ); - canvas.getContext('2d').drawImage(video, 0, 0); - return canvas.toBlob(function(snapshot) { - snapshot.name = post.file.name.replace(/\.\w+$/, '') + '.png'; - QR.handleFiles([snapshot]); - return QR.oekaki.edit(); - }); - }); - return video.currentTime = currentTime; - }); - $.on(video, 'error', () => QR.openError()); - return video.src = URL.createObjectURL(blob); - } else { - blob.name = post.file.name; - QR.handleFiles([blob]); - return QR.oekaki.edit(); - } - }); - } - }, - - setup() { - return $.global(function() { - const {FCX} = window; - FCX.oekakiCB = () => window.Tegaki.flatten().toBlob(function(file) { - const source = `oekaki-${Date.now()}`; - FCX.oekakiLatest = source; - return document.dispatchEvent(new CustomEvent('QRSetFile', { - bubbles: true, - detail: {file, name: FCX.oekakiName, source} - }));}); - if (window.Tegaki) { - return document.querySelector('#qr .oekaki').hidden = false; - } - }); - }, - - load(cb) { - if ($('script[src^="//s.4cdn.org/js/tegaki"]', d.head)) { - return cb(); - } else { - const style = $.el('link', { - rel: 'stylesheet', - href: `//s.4cdn.org/css/tegaki.${Date.now()}.css` - } - ); - const script = $.el('script', - {src: `//s.4cdn.org/js/tegaki.min.${Date.now()}.js`}); - let n = 0; - const onload = function() { - if (++n === 2) { return cb(); } - }; - $.on(style, 'load', onload); - $.on(script, 'load', onload); - return $.add(d.head, [style, script]); - } - }, - - draw() { - return $.global(function() { - const {Tegaki, FCX} = window; - if (Tegaki.bg) { Tegaki.destroy(); } - FCX.oekakiName = 'tegaki.png'; - return Tegaki.open({ - onDone: FCX.oekakiCB, - onCancel() { return Tegaki.bgColor = '#ffffff'; }, - width: +document.querySelector('#qr [name=oekaki-width]').value, - height: +document.querySelector('#qr [name=oekaki-height]').value, - bgColor: - document.querySelector('#qr [name=oekaki-bg]').checked ? - document.querySelector('#qr [name=oekaki-bgcolor]').value - : - 'transparent' - }); - }); - }, - - button() { - if (QR.selected.file) { - return QR.oekaki.edit(); - } else { - return QR.oekaki.toggle(); - } - }, - - edit() { - return QR.oekaki.load(() => $.global(function() { - const {Tegaki, FCX} = window; - const name = document.getElementById('qr-filename').value.replace(/\.\w+$/, '') + '.png'; - const {source} = document.getElementById('file-n-submit').dataset; - const error = content => document.dispatchEvent(new CustomEvent('CreateNotification', { - bubbles: true, - detail: {type: 'warning', content, lifetime: 20} - })); - var cb = function(e) { - if (e) { this.removeEventListener('QRMetadata', cb, false); } - const selected = document.getElementById('selected'); - if (!selected?.dataset.type) { return error('No file to edit.'); } - if (!/^(image|video)\//.test(selected.dataset.type)) { return error('Not an image.'); } - if (!selected.dataset.height) { return error('Metadata not available.'); } - if (selected.dataset.height === 'loading') { - selected.addEventListener('QRMetadata', cb, false); - return; - } - if (Tegaki.bg) { Tegaki.destroy(); } - FCX.oekakiName = name; - Tegaki.open({ - onDone: FCX.oekakiCB, - onCancel() { return Tegaki.bgColor = '#ffffff'; }, - width: +selected.dataset.width, - height: +selected.dataset.height, - bgColor: 'transparent' - }); - const canvas = document.createElement('canvas'); - canvas.width = (canvas.naturalWidth = +selected.dataset.width); - canvas.height = (canvas.naturalHeight = +selected.dataset.height); - canvas.hidden = true; - document.body.appendChild(canvas); - canvas.addEventListener('QRImageDrawn', function() { - this.remove(); - return Tegaki.onOpenImageLoaded.call(this); - } - , false); - return canvas.dispatchEvent(new CustomEvent('QRDrawFile', {bubbles: true})); - }; - if (Tegaki.bg && (Tegaki.onDoneCb === FCX.oekakiCB) && (source === FCX.oekakiLatest)) { - FCX.oekakiName = name; - return Tegaki.resume(); - } else { - return cb(); - } - })); - }, - - toggle() { - return QR.oekaki.load(() => QR.nodes.oekaki.hidden = !QR.nodes.oekaki.hidden); - } -}; diff --git a/src/Posting/QR.persona.js b/src/Posting/QR.persona.js deleted file mode 100644 index 174daf4e49..0000000000 --- a/src/Posting/QR.persona.js +++ /dev/null @@ -1,93 +0,0 @@ -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS104: Avoid inline assignments - * DS204: Change includes calls to have a more natural evaluation order - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -QR.persona = { - always: {}, - types: { - name: [], - email: [], - sub: [] - }, - - init() { - if (!Conf['Quick Reply'] && (!Conf['Menu'] || !Conf['Delete Link'])) { return; } - for (var item of Conf['QR.personas'].split('\n')) { - QR.persona.parseItem(item.trim()); - } - }, - - parseItem(item) { - let match, needle, type, val; - if (item[0] === '#') { return; } - if (!(match = item.match(/(name|options|email|subject|password):"(.*)"/i))) { return; } - [match, type, val] = Array.from(match); - - // Don't mix up item settings with val. - item = item.replace(match, ''); - - const boards = item.match(/boards:([^;]+)/i)?.[1].toLowerCase() || 'global'; - if ((boards !== 'global') && (needle = g.BOARD.ID, !boards.split(',').includes(needle))) { return; } - - - if (type === 'password') { - QR.persona.pwd = val; - return; - } - - if (type === 'options') { type = 'email'; } - if (type === 'subject') { type = 'sub'; } - - if (/always/i.test(item)) { - QR.persona.always[type] = val; - } - - if (!QR.persona.types[type].includes(val)) { - return QR.persona.types[type].push(val); - } - }, - - load() { - for (var type in QR.persona.types) { - var arr = QR.persona.types[type]; - var list = $(`#list-${type}`, QR.nodes.el); - for (var val of arr) { - if (val) { - $.add(list, $.el('option', - {textContent: val}) - ); - } - } - } - }, - - getPassword() { - let m; - if (QR.persona.pwd != null) { - return QR.persona.pwd; - } else if (m = d.cookie.match(/4chan_pass=([^;]+)/)) { - return decodeURIComponent(m[1]); - } else { - return ''; - } - }, - - get(cb) { - return $.get('QR.persona', {}, ({'QR.persona': persona}) => cb(persona)); - }, - - set(post) { - return $.get('QR.persona', {}, function({'QR.persona': persona}) { - persona = { - name: post.name, - flag: post.flag - }; - return $.set('QR.persona', persona); - }); - } -}; diff --git a/src/Posting/QR.post.js b/src/Posting/QR.post.js deleted file mode 100644 index 41c3c29a23..0000000000 --- a/src/Posting/QR.post.js +++ /dev/null @@ -1,517 +0,0 @@ -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS205: Consider reworking code to avoid use of IIFEs - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -QR.post = class { - constructor(select) { - this.select = this.select.bind(this); - const el = $.el('a', { - className: 'qr-preview', - draggable: true, - href: 'javascript:;' - } - ); - $.extend(el, {innerHTML: ''}); - - this.nodes = { - el, - rm: el.firstChild, - spoiler: $('.qr-preview-spoiler input', el), - span: el.lastChild - }; - - $.on(el, 'click', this.select); - $.on(this.nodes.rm, 'click', e => { e.stopPropagation(); return this.rm(); }); - $.on(this.nodes.spoiler, 'change', e => { - this.spoiler = e.target.checked; - if (this === QR.selected) { QR.nodes.spoiler.checked = this.spoiler; } - return this.preventAutoPost(); - }); - for (var label of $$('label', el)) { - $.on(label, 'click', e => e.stopPropagation()); - } - $.add(QR.nodes.dumpList, el); - - for (var event of ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop']) { - $.on(el, event.toLowerCase(), this[event]); - } - - this.thread = g.VIEW === 'thread' ? - g.THREADID - : - 'new'; - - const prev = QR.posts[QR.posts.length - 1]; - QR.posts.push(this); - this.nodes.spoiler.checked = (this.spoiler = prev && Conf['Remember Spoiler'] ? - prev.spoiler - : - false); - QR.persona.get(persona => { - this.name = 'name' in QR.persona.always ? - QR.persona.always.name - : prev ? - prev.name - : - persona.name; - - this.email = 'email' in QR.persona.always ? - QR.persona.always.email - : - ''; - - this.sub = 'sub' in QR.persona.always ? - QR.persona.always.sub - : - ''; - - if (QR.nodes.flag) { - this.flag = (() => { - if (prev) { - return prev.flag; - } else if (persona.flag && persona.flag in g.BOARD.config.board_flags) { - return persona.flag; - } - })(); - } - if (QR.selected === this) { return this.load(); } - }); // load persona - if (select) { this.select(); } - this.unlock(); - QR.captcha.moreNeeded(); - } - - rm() { - this.delete(); - const index = QR.posts.indexOf(this); - if (QR.posts.length === 1) { - new QR.post(true); - $.rmClass(QR.nodes.el, 'dump'); - } else if (this === QR.selected) { - (QR.posts[index-1] || QR.posts[index+1]).select(); - } - QR.posts.splice(index, 1); - QR.status(); - return QR.captcha.updateThread?.(); - } - - delete() { - $.rm(this.nodes.el); - URL.revokeObjectURL(this.URL); - return this.dismissErrors(); - } - - lock(lock=true) { - this.isLocked = lock; - if (this !== QR.selected) { return; } - for (var name of ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag']) { - var node; - if ((node = QR.nodes[name])) { - node.disabled = lock; - } - } - this.nodes.rm.style.visibility = lock ? 'hidden' : ''; - this.nodes.spoiler.disabled = lock; - return this.nodes.el.draggable = !lock; - } - - unlock() { - return this.lock(false); - } - - select() { - if (QR.selected) { - QR.selected.nodes.el.removeAttribute('id'); - QR.selected.forceSave(); - } - QR.selected = this; - this.lock(this.isLocked); - this.nodes.el.id = 'selected'; - // Scroll the list to center the focused post. - const rectEl = this.nodes.el.getBoundingClientRect(); - const rectList = this.nodes.el.parentNode.getBoundingClientRect(); - this.nodes.el.parentNode.scrollLeft += (rectEl.left + (rectEl.width/2)) - rectList.left - (rectList.width/2); - return this.load(); - } - - load() { - // Load this post's values. - - for (var name of ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag']) { - var node; - if (!(node = QR.nodes[name])) { continue; } - node.value = this[name] || node.dataset.default || ''; - } - - (this.thread !== 'new' ? $.addClass : $.rmClass)(QR.nodes.el, 'reply-to-thread'); - - this.showFileData(); - return QR.characterCount(); - } - - save(input, forced) { - if (input.type === 'checkbox') { - this.spoiler = input.checked; - return; - } - const {name} = input.dataset; - if (!['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag'].includes(name)) { return; } - const prev = this[name] || input.dataset.default || null; - this[name] = input.value || input.dataset.default || null; - switch (name) { - case 'thread': - (this.thread !== 'new' ? $.addClass : $.rmClass)(QR.nodes.el, 'reply-to-thread'); - QR.status(); - QR.captcha.updateThread?.(); - break; - case 'com': - this.updateComment(); - break; - case 'filename': - if (!this.file) { return; } - this.saveFilename(); - this.updateFilename(); - break; - case 'name': case 'flag': - if (this[name] !== prev) { // only save manual changes, not values filled in by persona settings - QR.persona.set(this); - } - break; - } - if (!forced) { return this.preventAutoPost(); } - } - - forceSave() { - if (this !== QR.selected) { return; } - // Do this in case people use extensions - // that do not trigger the `input` event. - for (var name of ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler', 'flag']) { - var node; - if (!(node = QR.nodes[name])) { continue; } - this.save(node, true); - } - } - - preventAutoPost() { - // Disable auto-posting if you're editing the first post - // during the last 5 seconds of the cooldown. - if (QR.cooldown.auto && (this === QR.posts[0])) { - QR.cooldown.update(); // adding/removing file can change cooldown - if (QR.cooldown.seconds <= 5) { return QR.cooldown.auto = false; } - } - } - - setComment(com) { - this.com = com || null; - if (this === QR.selected) { - QR.nodes.com.value = this.com; - } - return this.updateComment(); - } - - updateComment() { - if (this === QR.selected) { - QR.characterCount(); - } - this.nodes.span.textContent = this.com; - QR.captcha.moreNeeded(); - if (QR.captcha === Captcha.v2) { - return Captcha.cache.prerequest(); - } - } - - isOnlyQuotes() { - return (this.com || '').trim() === (this.quotedText || '').trim(); - } - - static rmErrored(e) { - e.stopPropagation(); - for (let i = QR.posts.length - 1; i >= 0; i--) { - var errors; - var post = QR.posts[i]; - if ((errors = post.errors)) { - for (var error of errors) { - if (doc.contains(error)) { - post.rm(); - break; - } - } - } - } - } - - error(className, message, link) { - const div = $.el('div', {className}); - $.extend(div, {innerHTML: '${message}?{link}{ [More info]}
[delete post] [delete all]'}); - (this.errors || (this.errors = [])).push(div); - const [rm, rmAll] = Array.from($$('a', div)); - $.on(div, 'click', () => { - if (QR.posts.includes(this)) { return this.select(); } - }); - $.on(rm, 'click', e => { - e.stopPropagation(); - if (QR.posts.includes(this)) { return this.rm(); } - }); - $.on(rmAll, 'click', QR.post.rmErrored); - return QR.error(div, true); - } - - fileError(message, link) { - return this.error('file-error', `${this.filename}: ${message}`, link); - } - - dismissErrors(test = () => true) { - if (this.errors) { - for (var error of this.errors) { - if (doc.contains(error) && test(error)) { - error.parentNode.previousElementSibling.click(); - } - } - } - } - - setFile(file) { - this.file = file; - if (Conf['Randomize Filename'] && (g.BOARD.ID !== 'f')) { - let ext; - this.filename = `${Date.now() - Math.floor(Math.random() * 365 * $.DAY)}`; - if (ext = this.file.name.match(QR.validExtension)) { this.filename += ext[0]; } - } else { - this.filename = this.file.name; - } - this.filesize = $.bytesToString(this.file.size); - this.checkSize(); - $.addClass(this.nodes.el, 'has-file'); - QR.captcha.moreNeeded(); - URL.revokeObjectURL(this.URL); - this.saveFilename(); - if (this === QR.selected) { - this.showFileData(); - } else { - this.updateFilename(); - } - this.rmMetadata(); - this.nodes.el.dataset.type = this.file.type; - this.nodes.el.style.backgroundImage = ''; - if (!QR.mimeTypes.includes(this.file.type)) { - this.fileError('Unsupported file type.'); - } else if (/^(image|video)\//.test(this.file.type)) { - this.readFile(); - } - return this.preventAutoPost(); - } - - checkSize() { - let max = QR.max_size; - if (/^video\//.test(this.file.type)) { max = Math.min(max, QR.max_size_video); } - if (this.file.size > max) { - return this.fileError(`File too large (file: ${this.filesize}, max: ${$.bytesToString(max)}).`); - } - } - - readFile() { - const isVideo = /^video\//.test(this.file.type); - const el = $.el(isVideo ? 'video' : 'img'); - if (isVideo && !el.canPlayType(this.file.type)) { return; } - - const event = isVideo ? 'loadeddata' : 'load'; - var onload = () => { - $.off(el, event, onload); - $.off(el, 'error', onerror); - this.checkDimensions(el); - this.setThumbnail(el); - return $.event('QRMetadata', null, this.nodes.el); - }; - var onerror = () => { - $.off(el, event, onload); - $.off(el, 'error', onerror); - this.fileError(`Corrupt ${isVideo ? 'video' : 'image'} or error reading metadata.`, '<%= meta.faq %>#error-reading-metadata'); - URL.revokeObjectURL(el.src); - // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=1021289 - this.nodes.el.removeAttribute('data-height'); - return $.event('QRMetadata', null, this.nodes.el); - }; - this.nodes.el.dataset.height = 'loading'; - $.on(el, event, onload); - $.on(el, 'error', onerror); - return el.src = URL.createObjectURL(this.file); - } - - checkDimensions(el) { - let height, width; - if (el.tagName === 'IMG') { - ({height, width} = el); - this.nodes.el.dataset.height = height; - this.nodes.el.dataset.width = width; - if ((height > QR.max_height) || (width > QR.max_width)) { - this.fileError(`Image too large (image: ${height}x${width}px, max: ${QR.max_height}x${QR.max_width}px)`); - } - if ((height < QR.min_height) || (width < QR.min_width)) { - return this.fileError(`Image too small (image: ${height}x${width}px, min: ${QR.min_height}x${QR.min_width}px)`); - } - } else { - const {videoHeight, videoWidth, duration} = el; - this.nodes.el.dataset.height = videoHeight; - this.nodes.el.dataset.width = videoWidth; - this.nodes.el.dataset.duration = duration; - const max_height = Math.min(QR.max_height, QR.max_height_video); - const max_width = Math.min(QR.max_width, QR.max_width_video); - if ((videoHeight > max_height) || (videoWidth > max_width)) { - this.fileError(`Video too large (video: ${videoHeight}x${videoWidth}px, max: ${max_height}x${max_width}px)`); - } - if ((videoHeight < QR.min_height) || (videoWidth < QR.min_width)) { - this.fileError(`Video too small (video: ${videoHeight}x${videoWidth}px, min: ${QR.min_height}x${QR.min_width}px)`); - } - if (!isFinite(duration)) { - this.fileError('Video lacks duration metadata (try remuxing)'); - } else if (duration > QR.max_duration_video) { - this.fileError(`Video too long (video: ${duration}s, max: ${QR.max_duration_video}s)`); - } - if (BoardConfig.noAudio(g.BOARD.ID) && $.hasAudio(el)) { - return this.fileError('Audio not allowed'); - } - } - } - - setThumbnail(el) { - // Create a redimensioned thumbnail. - let height, width; - const isVideo = el.tagName === 'VIDEO'; - - // Generate thumbnails only if they're really big. - // Resized pictures through canvases look like ass, - // so we generate thumbnails `s` times bigger then expected - // to avoid crappy resized quality. - let s = 90 * 2 * window.devicePixelRatio; - if (this.file.type === 'image/gif') { s *= 3; } // let them animate - if (isVideo) { - height = el.videoHeight; - width = el.videoWidth; - } else { - ({height, width} = el); - if ((height < s) || (width < s)) { - this.URL = el.src; - this.nodes.el.style.backgroundImage = `url(${this.URL})`; - return; - } - } - - if (height <= width) { - width = (s / height) * width; - height = s; - } else { - height = (s / width) * height; - width = s; - } - const cv = $.el('canvas'); - cv.height = height; - cv.width = width; - cv.getContext('2d').drawImage(el, 0, 0, width, height); - URL.revokeObjectURL(el.src); - return cv.toBlob(blob => { - this.URL = URL.createObjectURL(blob); - return this.nodes.el.style.backgroundImage = `url(${this.URL})`; - }); - } - - rmFile() { - if (this.isLocked) { return; } - delete this.file; - delete this.filename; - delete this.filesize; - this.nodes.el.removeAttribute('title'); - QR.nodes.filename.removeAttribute('title'); - this.rmMetadata(); - this.nodes.el.style.backgroundImage = ''; - $.rmClass(this.nodes.el, 'has-file'); - this.showFileData(); - URL.revokeObjectURL(this.URL); - this.dismissErrors(error => $.hasClass(error, 'file-error')); - return this.preventAutoPost(); - } - - rmMetadata() { - for (var attr of ['type', 'height', 'width', 'duration']) { - // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=1021289 - this.nodes.el.removeAttribute(`data-${attr}`); - } - } - - saveFilename() { - this.file.newName = (this.filename || '').replace(/[/\\]/g, '-'); - if (!QR.validExtension.test(this.filename)) { - // 4chan will truncate the filename if it has no extension. - return this.file.newName += `.${$.getOwn(QR.extensionFromType, this.file.type) || 'jpg'}`; - } - } - - updateFilename() { - const long = `${this.filename} (${this.filesize})`; - this.nodes.el.title = long; - if (this !== QR.selected) { return; } - return QR.nodes.filename.title = long; - } - - showFileData() { - if (this.file) { - this.updateFilename(); - QR.nodes.filename.value = this.filename; - $.addClass(QR.nodes.oekaki, 'has-file'); - $.addClass(QR.nodes.fileSubmit, 'has-file'); - } else { - $.rmClass(QR.nodes.oekaki, 'has-file'); - $.rmClass(QR.nodes.fileSubmit, 'has-file'); - } - if (this.file?.source != null) { - QR.nodes.fileSubmit.dataset.source = this.file.source; - } else { - QR.nodes.fileSubmit.removeAttribute('data-source'); - } - return QR.nodes.spoiler.checked = this.spoiler; - } - - pasteText(file) { - this.pasting = true; - this.preventAutoPost(); - const reader = new FileReader(); - reader.onload = e => { - const {result} = e.target; - this.setComment((this.com ? `${this.com}\n${result}` : result)); - return delete this.pasting; - }; - return reader.readAsText(file); - } - - dragStart(e) { - const {left, top} = this.getBoundingClientRect(); - e.dataTransfer.setDragImage(this, e.clientX - left, e.clientY - top); - return $.addClass(this, 'drag'); - } - dragEnd() { return $.rmClass(this, 'drag'); } - dragEnter() { return $.addClass(this, 'over'); } - dragLeave() { return $.rmClass(this, 'over'); } - - dragOver(e) { - e.preventDefault(); - return e.dataTransfer.dropEffect = 'move'; - } - - drop() { - $.rmClass(this, 'over'); - if (!this.draggable) { return; } - const el = $('.drag', this.parentNode); - const index = el => [...Array.from(el.parentNode.children)].indexOf(el); - const oldIndex = index(el); - const newIndex = index(this); - if (QR.posts[oldIndex].isLocked || QR.posts[newIndex].isLocked) { return; } - (oldIndex < newIndex ? $.after : $.before)(this, el); - const post = QR.posts.splice(oldIndex, 1)[0]; - QR.posts.splice(newIndex, 0, post); - QR.status(); - return QR.captcha.updateThread?.(); - } -}; diff --git a/src/Quotelinks/QuoteBacklink.js b/src/Quotelinks/QuoteBacklink.js index 213cb5c905..a0fe8b646d 100644 --- a/src/Quotelinks/QuoteBacklink.js +++ b/src/Quotelinks/QuoteBacklink.js @@ -1,3 +1,11 @@ +import Callbacks from "../classes/Callbacks"; +import { g, Conf, doc } from "../globals/globals"; +import $ from "../platform/$"; +import { dict } from "../platform/helpers"; +import QuoteInline from "./QuoteInline"; +import QuotePreview from "./QuotePreview"; +import QuoteYou from "./QuoteYou"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -15,7 +23,7 @@ var QuoteBacklink = { // Second callback adds relevant containers into posts. // This is is so that fetched posts can get their backlinks, // and that as much backlinks are appended in the background as possible. - containers: $.dict(), + containers: dict(), init() { if (!['index', 'thread'].includes(g.VIEW) || !Conf['Quote Backlinks']) { return; } @@ -91,3 +99,4 @@ var QuoteBacklink = { (this.containers[id] = $.el('span', {className: 'container'})); } }; +export default QuoteBacklink; diff --git a/src/Quotelinks/QuoteCT.js b/src/Quotelinks/QuoteCT.js index 2af14e5c47..00eaba5224 100644 --- a/src/Quotelinks/QuoteCT.js +++ b/src/Quotelinks/QuoteCT.js @@ -1,3 +1,9 @@ +import $ from "../platform/$"; +import Callbacks from "../classes/Callbacks"; +import ExpandComment from "../Miscellaneous/ExpandComment"; +import { g, Conf } from "../globals/globals"; +import Get from "../General/Get"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -39,3 +45,4 @@ var QuoteCT = { } } }; +export default QuoteCT; diff --git a/src/Quotelinks/QuoteInline.js b/src/Quotelinks/QuoteInline.js index 1d47fdc327..131f2d7e2b 100644 --- a/src/Quotelinks/QuoteInline.js +++ b/src/Quotelinks/QuoteInline.js @@ -1,3 +1,11 @@ +import Callbacks from "../classes/Callbacks"; +import Fetcher from "../classes/Fetcher"; +import Get from "../General/Get"; +import { g, Conf, doc } from "../globals/globals"; +import ExpandComment from "../Miscellaneous/ExpandComment"; +import Unread from "../Monitoring/Unread"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -143,3 +151,4 @@ var QuoteInline = { } } }; +export default QuoteInline; diff --git a/src/Quotelinks/QuoteOP.js b/src/Quotelinks/QuoteOP.js index beb953a10a..5c986f8e64 100644 --- a/src/Quotelinks/QuoteOP.js +++ b/src/Quotelinks/QuoteOP.js @@ -1,3 +1,9 @@ +import Callbacks from "../classes/Callbacks"; +import Get from "../General/Get"; +import { g, Conf } from "../globals/globals"; +import ExpandComment from "../Miscellaneous/ExpandComment"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -52,3 +58,4 @@ var QuoteOP = { } } }; +export default QuoteOP; diff --git a/src/Quotelinks/QuotePreview.js b/src/Quotelinks/QuotePreview.js index a73d822f1b..9128704a59 100644 --- a/src/Quotelinks/QuotePreview.js +++ b/src/Quotelinks/QuotePreview.js @@ -1,3 +1,12 @@ +import Callbacks from "../classes/Callbacks"; +import Fetcher from "../classes/Fetcher"; +import Get from "../General/Get"; +import Header from "../General/Header"; +import UI from "../General/UI"; +import { Conf, d, g } from "../globals/globals"; +import ExpandComment from "../Miscellaneous/ExpandComment"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -84,3 +93,4 @@ var QuotePreview = { } } }; +export default QuotePreview; diff --git a/src/Quotelinks/QuoteStrikeThrough.js b/src/Quotelinks/QuoteStrikeThrough.js index 2cb38efb48..ceabbef4df 100644 --- a/src/Quotelinks/QuoteStrikeThrough.js +++ b/src/Quotelinks/QuoteStrikeThrough.js @@ -1,3 +1,8 @@ +import Callbacks from "../classes/Callbacks"; +import Get from "../General/Get"; +import { g, Conf } from "../globals/globals"; +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -24,3 +29,4 @@ const QuoteStrikeThrough = { } } }; +export default QuoteStrikeThrough; diff --git a/src/Quotelinks/QuoteThreading.js b/src/Quotelinks/QuoteThreading.js index b170da2597..4cf265fdc6 100644 --- a/src/Quotelinks/QuoteThreading.js +++ b/src/Quotelinks/QuoteThreading.js @@ -1,3 +1,12 @@ +import Callbacks from "../classes/Callbacks"; +import RandomAccessList from "../classes/RandomAccessList"; +import Header from "../General/Header"; +import { Conf, d, g } from "../globals/globals"; +import ReplyPruning from "../Monitoring/ReplyPruning"; +import Unread from "../Monitoring/Unread"; +import $ from "../platform/$"; +import { dict } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -46,9 +55,9 @@ var QuoteThreading = { }); }, - parent: $.dict(), - children: $.dict(), - inserted: $.dict(), + parent: dict(), + children: dict(), + inserted: dict(), toggleThreading() { return this.setThreadingState(!Conf['Thread Quotes']); @@ -181,7 +190,7 @@ var QuoteThreading = { } else { const nodes = []; Unread.order = new RandomAccessList(); - QuoteThreading.inserted = $.dict(); + QuoteThreading.inserted = dict(); posts.forEach(function(post) { if (post.isFetchedQuote) { return; } Unread.order.push(post); @@ -203,3 +212,4 @@ var QuoteThreading = { return Unread.update(); } }; +export default QuoteThreading; diff --git a/src/Quotelinks/QuoteYou.js b/src/Quotelinks/QuoteYou.js index f44807f2b3..b6d9b9a6fd 100644 --- a/src/Quotelinks/QuoteYou.js +++ b/src/Quotelinks/QuoteYou.js @@ -1,3 +1,15 @@ +import Callbacks from "../classes/Callbacks"; +import DataBoard from "../classes/DataBoard"; +import Notice from "../classes/Notice"; +import Get from "../General/Get"; +import Header from "../General/Header"; +import { Conf, d, doc, g } from "../globals/globals"; +import Menu from "../Menu/Menu"; +import ExpandComment from "../Miscellaneous/ExpandComment"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import PostRedirect from "../Posting/PostRedirect"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -164,3 +176,4 @@ var QuoteYou = { } } }; +export default QuoteYou; diff --git a/src/Quotelinks/Quotify.js b/src/Quotelinks/Quotify.js index 4ed31e708f..6f11c740fa 100644 --- a/src/Quotelinks/Quotify.js +++ b/src/Quotelinks/Quotify.js @@ -1,3 +1,11 @@ +import Redirect from "../Archive/Redirect"; +import Callbacks from "../classes/Callbacks"; +import Post from "../classes/Post"; +import { g, Conf, doc } from "../globals/globals"; +import ExpandComment from "../Miscellaneous/ExpandComment"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -138,3 +146,4 @@ var Quotify = { return $.replace(deadlink, [...Array.from(deadlink.childNodes)]); } }; +export default Quotify; diff --git a/src/classes/Board.js b/src/classes/Board.js index 02dc8b1c1b..88e55a5cb3 100644 --- a/src/classes/Board.js +++ b/src/classes/Board.js @@ -1,9 +1,13 @@ +import BoardConfig from "../General/BoardConfig"; +import { d, g } from "../globals/globals"; +import SimpleDict from "./SimpleDict"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -class Board { +export default class Board { toString() { return this.ID; } constructor(ID) { diff --git a/src/classes/Callbacks.js b/src/classes/Callbacks.js index 143c794af3..24aed5edba 100644 --- a/src/classes/Callbacks.js +++ b/src/classes/Callbacks.js @@ -1,10 +1,12 @@ +import Main from "../main/Main"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * DS206: Consider reworking classes to avoid initClass * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -class Callbacks { +export default class Callbacks { static initClass() { this.Post = new Callbacks('Post'); this.Thread = new Callbacks('Thread'); diff --git a/src/classes/CatalogThread.js b/src/classes/CatalogThread.js index e6337a4a3d..510308280d 100644 --- a/src/classes/CatalogThread.js +++ b/src/classes/CatalogThread.js @@ -1,4 +1,6 @@ -class CatalogThread { +import $ from "../platform/$"; + +export default class CatalogThread { toString() { return this.ID; } constructor(root, thread) { diff --git a/src/classes/CatalogThreadNative.js b/src/classes/CatalogThreadNative.js index dfe097aaf6..93c8696fd4 100644 --- a/src/classes/CatalogThreadNative.js +++ b/src/classes/CatalogThreadNative.js @@ -1,4 +1,9 @@ -class CatalogThreadNative { +import { g } from "../globals/globals"; +import $ from "../platform/$"; +import Board from "./Board"; +import Thread from "./Thread"; + +export default class CatalogThreadNative { toString() { return this.ID; } constructor(root) { diff --git a/src/classes/Connection.js b/src/classes/Connection.js index 2ea41ba3d5..58a280e430 100644 --- a/src/classes/Connection.js +++ b/src/classes/Connection.js @@ -1,9 +1,12 @@ +import $ from "../platform/$"; +import { g } from "../globals/globals"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -class Connection { +export default class Connection { constructor(target, origin, cb={}) { this.send = this.send.bind(this); this.onMessage = this.onMessage.bind(this); diff --git a/src/classes/DataBoard.js b/src/classes/DataBoard.js index 03e9938a1f..55de86e63d 100644 --- a/src/classes/DataBoard.js +++ b/src/classes/DataBoard.js @@ -1,3 +1,7 @@ +import { Conf, d, g } from "../globals/globals"; +import $ from "../platform/$"; +import { dict, HOUR } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -6,12 +10,9 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -class DataBoard { - static initClass() { - this.keys = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads', 'watcherLastModified', 'customTitles']; - - this.prototype.changes = []; - } +export default class DataBoard { + static keys = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads', 'watcherLastModified', 'customTitles']; + static changes = []; constructor(key, sync, dontClean) { this.onSync = this.onSync.bind(this); @@ -39,20 +40,20 @@ class DataBoard { delete this.data.boards; delete this.data.lastChecked; } - return this.data[g.SITE.ID] || (this.data[g.SITE.ID] = {boards: $.dict()}); + return this.data[g.SITE.ID] || (this.data[g.SITE.ID] = { boards: dict() }); } save(change, cb) { change(); - this.changes.push(change); - return $.get(this.key, {boards: $.dict()}, items => { - if (!this.changes.length) { return; } + DataBoard.changes.push(change); + return $.get(this.key, { boards: dict() }, items => { + if (!DataBoard.changes.length) { return; } const needSync = ((items[this.key].version || 0) > (this.data.version || 0)); if (needSync) { this.initData(items[this.key]); - for (change of this.changes) { change(); } + for (change of DataBoard.changes) { change(); } } - this.changes = []; + DataBoard.changes = []; this.data.version = (this.data.version || 0) + 1; return $.set(this.key, this.data, () => { if (needSync) { this.sync?.(); } @@ -62,10 +63,10 @@ class DataBoard { } forceSync(cb) { - return $.get(this.key, {boards: $.dict()}, items => { + return $.get(this.key, { boards: dict() }, items => { if ((items[this.key].version || 0) > (this.data.version || 0)) { this.initData(items[this.key]); - for (var change of this.changes) { change(); } + for (var change of DataBoard.changes) { change(); } this.sync?.(); } return cb?.(); @@ -112,12 +113,12 @@ class DataBoard { setUnsafe({siteID, boardID, threadID, postID, val}) { if (!siteID) { siteID = g.SITE.ID; } - if (!this.data[siteID]) { this.data[siteID] = {boards: $.dict()}; } + if (!this.data[siteID]) { this.data[siteID] = { boards: dict() }; } if (postID !== undefined) { let base; - return (((base = this.data[siteID].boards[boardID] || (this.data[siteID].boards[boardID] = $.dict())))[threadID] || (base[threadID] = $.dict()))[postID] = val; + return (((base = this.data[siteID].boards[boardID] || (this.data[siteID].boards[boardID] = dict())))[threadID] || (base[threadID] = dict()))[postID] = val; } else if (threadID !== undefined) { - return (this.data[siteID].boards[boardID] || (this.data[siteID].boards[boardID] = $.dict()))[threadID] = val; + return (this.data[siteID].boards[boardID] || (this.data[siteID].boards[boardID] = dict()))[threadID] = val; } else { return this.data[siteID].boards[boardID] = val; } @@ -125,7 +126,7 @@ class DataBoard { extend({siteID, boardID, threadID, postID, val}, cb) { return this.save(() => { - const oldVal = this.get({siteID, boardID, threadID, postID, defaultValue: $.dict()}); + const oldVal = this.get({ siteID, boardID, threadID, postID, defaultValue: dict() }); for (var key in val) { var subVal = val[key]; if (typeof subVal === 'undefined') { @@ -180,7 +181,7 @@ class DataBoard { this.deleteIfEmpty({siteID, boardID}); } const now = Date.now(); - if (now - (2 * $.HOUR) >= ((middle = this.data[siteID].lastChecked || 0)) || middle > now) { + if (now - (2 * HOUR) >= ((middle = this.data[siteID].lastChecked || 0)) || middle > now) { this.data[siteID].lastChecked = now; for (boardID in this.data[siteID].boards) { this.ajaxClean(boardID); @@ -209,7 +210,7 @@ class DataBoard { let board, ID; const siteID = g.SITE.ID; if (!(board = this.data[siteID].boards[boardID])) { return; } - const threads = $.dict(); + const threads = dict(); if (response1) { for (var page of response1) { for (var thread of page.threads) { diff --git a/src/classes/Fetcher.js b/src/classes/Fetcher.js index 721547f243..4b2498770a 100644 --- a/src/classes/Fetcher.js +++ b/src/classes/Fetcher.js @@ -1,3 +1,16 @@ +import Redirect from "../Archive/Redirect"; +import Board from "./Board"; +import Post from "./Post"; +import Thread from "./Thread"; +import $ from "../platform/$"; +import Main from "../main/Main"; +import Index from "../General/Index"; +import { E, g, Conf, d } from "../globals/globals"; +import ImageHost from "../Images/ImageHost"; +import CrossOrigin from "../platform/CrossOrigin"; +import Get from "../General/Get"; +import { dict } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -7,7 +20,7 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -class Fetcher { +export default class Fetcher { static initClass() { this.prototype.archiveTags = { @@ -79,7 +92,8 @@ class Fetcher { $.add(nodes.root, nodes.post); // Indicate links to the containing post. - for (var quote of clone.nodes.quotelinks.concat([...Array.from(clone.nodes.backlinks)])) { + const quotes = [...clone.nodes.quotelinks, ...clone.nodes.backlinks]; + for (var quote of quotes) { var {boardID, postID} = Get.postDataFromLink(quote); if ((postID === this.quoter.ID) && (boardID === this.quoter.board.ID)) { $.addClass(quote, 'forwardlink'); @@ -218,7 +232,7 @@ class Fetcher { for (let i = 0; i < comment.length; i++) { var text = comment[i]; if ((i % 2) === 1) { - var tag = this.archiveTags[text.replace(/\ .*\]/, ']')]; + var tag = Fetcher.archiveTags[text.replace(/\ .*\]/, ']')]; if (typeof tag === 'function') { result.push(tag(text)); } else { result.push(tag); } } else { var greentext = text[0] === '>'; @@ -291,7 +305,7 @@ class Fetcher { if (!/\.pdf$/.test(o.file.url)) { o.file.dimensions = `${o.file.width}x${o.file.height}`; } if ((this.boardID === 'f') && data.media.exif) { o.file.tag = JSON.parse(data.media.exif).Tag; } } - o.extra = $.dict(); + o.extra = dict(); const board = g.boards[this.boardID] || new Board(this.boardID); diff --git a/src/classes/Notice.js b/src/classes/Notice.js index 8290f1643d..7e1f212fd5 100644 --- a/src/classes/Notice.js +++ b/src/classes/Notice.js @@ -1,9 +1,14 @@ +import Header from "../General/Header"; +import { d } from "../globals/globals"; +import $ from "../platform/$"; +import { SECOND } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -class Notice { +export default class Notice { constructor(type, content, timeout, onclose) { this.add = this.add.bind(this); this.close = this.close.bind(this); @@ -36,7 +41,7 @@ class Notice { $.add(Header.noticesRoot, this.el); this.el.clientHeight; // force reflow this.el.style.opacity = 1; - if (this.timeout) { return setTimeout(this.close, this.timeout * $.SECOND); } + if (this.timeout) { return setTimeout(this.close, this.timeout * SECOND); } } close() { diff --git a/src/classes/Post.Clone.js b/src/classes/Post.Clone.js deleted file mode 100644 index 9b78253789..0000000000 --- a/src/classes/Post.Clone.js +++ /dev/null @@ -1,112 +0,0 @@ -/* - * decaffeinate suggestions: - * DS002: Fix invalid constructor - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS206: Consider reworking classes to avoid initClass - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -Post.Clone = (function() { - const Cls = class extends Post { - static initClass() { - this.prototype.isClone = true; - } - - constructor() { - const that = Object.create(Post.Clone.prototype); - that.construct(...arguments); - return that; - } - - construct(origin, context, contractThumb) { - let file, fileRoots, key; - this.origin = origin; - this.context = context; - for (key of ['ID', 'postID', 'threadID', 'boardID', 'siteID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply']) { - // Copy or point to the origin's key value. - this[key] = this.origin[key]; - } - - const {nodes} = this.origin; - const root = contractThumb ? - this.cloneWithoutVideo(nodes.root) - : - nodes.root.cloneNode(true); - if (!Post.Clone.suffix) { Post.Clone.suffix = 0; } - for (var node of [root, ...Array.from($$('[id]', root))]) { - node.id += `_${Post.Clone.suffix}`; - } - Post.Clone.suffix++; - - // Remove inlined posts inside of this post. - for (var inline of $$('.inline', root)) { - $.rm(inline); - } - for (var inlined of $$('.inlined', root)) { - $.rmClass(inlined, 'inlined'); - } - - this.nodes = this.parseNodes(root); - - root.hidden = false; // post hiding - $.rmClass(root, 'forwarded'); // quote inlining - $.rmClass(this.nodes.post, 'highlight'); // keybind navigation, ID highlighting - - // Remove catalog stuff. - if (!this.isReply) { - this.setCatalogOP(false); - $.rm($('.catalog-link', this.nodes.post)); - $.rm($('.catalog-stats', this.nodes.post)); - $.rm($('.catalog-replies', this.nodes.post)); - } - - this.parseQuotes(); - this.quotes = [...Array.from(this.origin.quotes)]; - - this.files = []; - if (this.origin.files.length) { fileRoots = this.fileRoots(); } - for (var originFile of this.origin.files) { - // Copy values, point to relevant elements. - file = {}; - for (key in originFile) { - var val = originFile[key]; - file[key] = val; - } - var fileRoot = fileRoots[file.docIndex]; - for (key in g.SITE.selectors.file) { - var selector = g.SITE.selectors.file[key]; - file[key] = $(selector, fileRoot); - } - file.thumbLink = file.thumb?.parentNode; - if (file.thumbLink) { file.fullImage = $('.full-image', file.thumbLink); } - file.videoControls = $('.video-controls', file.text); - if (file.videoThumb) { file.thumb.muted = true; } - this.files.push(file); - } - - if (this.files.length) { - this.file = this.files[0]; - - // Contract thumbnails in quote preview - if (this.file.thumb && contractThumb) { ImageExpand.contract(this); } - } - - if (this.origin.isDead) { this.isDead = true; } - return root.dataset.clone = this.origin.clones.push(this) - 1; - } - - cloneWithoutVideo(node) { - if ((node.tagName === 'VIDEO') && !node.dataset.md5) { // (exception for WebM thumbnails) - return []; - } else if ((node.nodeType === Node.ELEMENT_NODE) && $('video', node)) { - const clone = node.cloneNode(false); - for (var child of node.childNodes) { $.add(clone, this.cloneWithoutVideo(child)); } - return clone; - } else { - return node.cloneNode(true); - } - } - }; - Cls.initClass(); - return Cls; -})(); diff --git a/src/classes/Post.js b/src/classes/Post.js index 1292990fb1..e73c5c67e8 100644 --- a/src/classes/Post.js +++ b/src/classes/Post.js @@ -1,20 +1,32 @@ +import Get from "../General/Get"; +import { g, Conf } from "../globals/globals"; +import ImageExpand from "../Images/ImageExpand"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import Callbacks from "./Callbacks"; + +/** + * Original coffeescript version added a child class that somehow skips the original constructor in the compilation + * output? In es classes we have to do tha manually. + */ +const skipConstructor = Symbol('skip post constructor'); + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * DS206: Consider reworking classes to avoid initClass * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -class Post { - static initClass() { - - this.deadMark = - // \u00A0 is nbsp - $.el('span', { - textContent: '\u00A0(Dead)', - className: 'qmark-dead' - } - ); - } +export default class Post { + // because of a circular dependency $ might not be initialized, so we can't use $.el + static deadMark = (() => { + const el = document.createElement('span'); + // \u00A0 is nbsp + el.textContent = '\u00A0(Dead)'; + el.className = 'qmark-dead'; + return el; + })(); + toString() { return this.ID; } constructor(root, thread, board, flags={}) { @@ -22,6 +34,9 @@ class Post { // @normalizedOriginal = Test.normalize root // <% } %> + if (root === skipConstructor) return; + + this.root = root; this.thread = thread; this.board = board; $.extend(this, flags); @@ -95,6 +110,9 @@ class Post { this.board.posts.push(this.ID, this); this.thread.posts.push(this.ID, this); g.posts.push(this.fullID, this); + + this.isFetchedQuote = false; + this.isClone = false; } parseNodes(root) { @@ -337,7 +355,7 @@ class Post { addClone(context, contractThumb) { // Callbacks may not have been run yet due to anti-browser-lock delay in Main.callbackNodesDB. Callbacks.Post.execute(this); - return new Post.Clone(this, context, contractThumb); + return new PostClone(skipConstructor).construct(this, context, contractThumb); } rmClone(index) { @@ -354,5 +372,95 @@ class Post { this.nodes.post.classList.toggle('op', !isCatalogOP); return this.nodes.post.style.left = (this.nodes.post.style.right = null); } -} -Post.initClass(); +}; + +export class PostClone extends Post { + static suffix = 0; + + constructor(root, thread, board, flags = {}) { + super(root, thread, board, flags = {}); + this.isClone = true; + } + + construct(origin, context, contractThumb) { + let file, fileRoots, key; + this.origin = origin; + this.context = context; + for (key of ['ID', 'postID', 'threadID', 'boardID', 'siteID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply']) { + // Copy or point to the origin's key value. + this[key] = this.origin[key]; + } + + const { nodes } = this.origin; + const root = contractThumb ? this.cloneWithoutVideo(nodes.root) : nodes.root.cloneNode(true); + for (var node of [root, ...$$('[id]', root)]) { + node.id += `_${PostClone.suffix}`; + } + PostClone.suffix++; + + // Remove inlined posts inside of this post. + for (var inline of $$('.inline', root)) { + $.rm(inline); + } + for (var inlined of $$('.inlined', root)) { + $.rmClass(inlined, 'inlined'); + } + + this.nodes = this.parseNodes(root); + + root.hidden = false; // post hiding + $.rmClass(root, 'forwarded'); // quote inlining + $.rmClass(this.nodes.post, 'highlight'); // keybind navigation, ID highlighting + + // Remove catalog stuff. + if (!this.isReply) { + this.setCatalogOP(false); + $.rm($('.catalog-link', this.nodes.post)); + $.rm($('.catalog-stats', this.nodes.post)); + $.rm($('.catalog-replies', this.nodes.post)); + } + + this.parseQuotes(); + this.quotes = [...this.origin.quotes]; + + this.files = []; + if (this.origin.files.length) { fileRoots = this.fileRoots(); } + for (var originFile of this.origin.files) { + // Copy values, point to relevant elements. + file = { ...originFile }; + var fileRoot = fileRoots[file.docIndex]; + for (key in g.SITE.selectors.file) { + var selector = g.SITE.selectors.file[key]; + file[key] = $(selector, fileRoot); + } + file.thumbLink = file.thumb?.parentNode; + if (file.thumbLink) { file.fullImage = $('.full-image', file.thumbLink); } + file.videoControls = $('.video-controls', file.text); + if (file.videoThumb) { file.thumb.muted = true; } + this.files.push(file); + } + + if (this.files.length) { + this.file = this.files[0]; + + // Contract thumbnails in quote preview + if (this.file.thumb && contractThumb) { ImageExpand.contract(this); } + } + + if (this.origin.isDead) { this.isDead = true; } + root.dataset.clone = this.origin.clones.push(this) - 1; + return this; + } + + cloneWithoutVideo(node) { + if ((node.tagName === 'VIDEO') && !node.dataset.md5) { // (exception for WebM thumbnails) + return []; + } else if ((node.nodeType === Node.ELEMENT_NODE) && $('video', node)) { + const clone = node.cloneNode(false); + for (var child of node.childNodes) { $.add(clone, this.cloneWithoutVideo(child)); } + return clone; + } else { + return node.cloneNode(true); + } + } +}; diff --git a/src/classes/RandomAccessList.js b/src/classes/RandomAccessList.js index 48c50e9c67..5a4dfa2e7a 100644 --- a/src/classes/RandomAccessList.js +++ b/src/classes/RandomAccessList.js @@ -3,7 +3,7 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -class RandomAccessList { +export default class RandomAccessList { constructor(items) { this.length = 0; if (items) { for (var item of items) { this.push(item); } } diff --git a/src/classes/SimpleDict.js b/src/classes/SimpleDict.ts similarity index 85% rename from src/classes/SimpleDict.js rename to src/classes/SimpleDict.ts index 6e0b9461ed..7ab5ca9020 100644 --- a/src/classes/SimpleDict.js +++ b/src/classes/SimpleDict.ts @@ -1,15 +1,19 @@ +import $ from "../platform/$"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -class SimpleDict { +export default class SimpleDict { + keys: string[] + constructor() { this.keys = []; } - push(key, data) { + push(key, data: T) { key = `${key}`; if (!this[key]) { this.keys.push(key); } return this[key] = data; @@ -28,7 +32,7 @@ class SimpleDict { for (var key of [...Array.from(this.keys)]) { fn(this[key]); } } - get(key) { + get(key): T { if (key === 'keys') { return undefined; } else { diff --git a/src/classes/Thread.js b/src/classes/Thread.js index 423f036428..2b340d5c22 100644 --- a/src/classes/Thread.js +++ b/src/classes/Thread.js @@ -1,9 +1,13 @@ +import SimpleDict from "./SimpleDict"; +import $ from "../platform/$"; +import { g } from "../globals/globals"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -class Thread { +export default class Thread { toString() { return this.ID; } constructor(ID, board) { diff --git a/src/config/Config.js b/src/config/Config.js index 374e365f88..22ca200b71 100644 --- a/src/config/Config.js +++ b/src/config/Config.js @@ -1,5 +1,6 @@ import userCss from './user.css'; -import banners from './banners.js'; +import banners from './banners.json'; +import meta from '../../package.json'; const Config = { main: { @@ -12,9 +13,9 @@ const Config = { true, 'Replace the original board index with one supporting searching, sorting, infinite scrolling, and a catalog mode.' ], - 'Use <%= meta.name %> Catalog': [ + [`Use ${meta.name} Catalog`]: [ true, - 'Link to <%= meta.name %>\'s catalog instead of the native 4chan one.', + `Link to ${meta.name}'s catalog instead of the native 4chan one.`, 1 ], 'Index Refresh Notifications': [ @@ -28,7 +29,7 @@ const Config = { ], 'Open Threads in New Tab': [ false, - 'Make links to threads in the index / <%= meta.name %> catalog open in a new tab.' + `Make links to threads in the index / ${meta.name} catalog open in a new tab.` ], 'External Catalog': [ false, @@ -44,7 +45,7 @@ const Config = { ], 'Desktop Notifications': [ true, - 'Enables desktop notifications across various <%= meta.name %> features.' + `Enables desktop notifications across various ${meta.name} features.` ], '404 Redirect': [ true, @@ -110,7 +111,7 @@ const Config = { ], 'Show Updated Notifications': [ true, - 'Show notifications when <%= meta.name %> is successfully updated.' + `Show notifications when ${meta.name} is successfully updated.` ], 'Color User IDs': [ true, @@ -142,7 +143,7 @@ const Config = { ], 'Disable Native Extension': [ true, - '<%= meta.name %> is NOT designed to work with the native extension.' + `${meta.name} is NOT designed to work with the native extension.` ], 'Enable Native Flash Embedding': [ true, @@ -235,7 +236,7 @@ const Config = { ], 'Image Hover in Catalog': [ true, - 'Show full image / video on mouseover in <%= meta.name %> catalog.' + `Show full image / video on mouseover in ${meta.name} catalog.` ], 'Gallery': [ true, @@ -322,7 +323,7 @@ const Config = { ], 'Volume in New Tab': [ true, - 'Apply <%= meta.name %> mute and volume settings to videos opened in their own tabs.' + `Apply ${meta.name} mute and volume settings to videos opened in their own tabs.` ] }, @@ -860,7 +861,7 @@ http://eye.swfchan.com/search/?q=%name;types:swf current-catalog-text:"Catalog" current-expired-text:"Expired" current-archive-text:"Archive"] -[external-text:"FAQ","<%= meta.faq %>"]\ +[external-text:"FAQ","${meta.name}"]\ `, QR: { @@ -1201,3 +1202,4 @@ https://*.hcaptcha.com 'PSAseen': [[]] }; +export default Config; diff --git a/src/config/banners.js b/src/config/banners.js deleted file mode 100644 index 9cf65d7426..0000000000 --- a/src/config/banners.js +++ /dev/null @@ -1,2 +0,0 @@ -const banners = ["0.jpg", "1.jpg", "2.jpg", "4.jpg", "6.jpg", "7.jpg", "8.jpg", "9.jpg", "10.jpg", "11.jpg", "12.jpg", "13.jpg", "14.jpg", "16.jpg", "17.jpg", "18.jpg", "19.jpg", "20.jpg", "21.jpg", "22.jpg", "24.jpg", "25.jpg", "26.jpg", "28.jpg", "29.jpg", "33.jpg", "38.jpg", "39.jpg", "43.jpg", "44.jpg", "45.jpg", "46.jpg", "47.jpg", "52.jpg", "54.jpg", "57.jpg", "59.jpg", "60.jpg", "61.jpg", "64.jpg", "66.jpg", "67.jpg", "69.jpg", "71.jpg", "72.jpg", "76.jpg", "77.jpg", "81.jpg", "82.jpg", "83.jpg", "84.jpg", "88.jpg", "90.jpg", "91.jpg", "96.jpg", "98.jpg", "99.jpg", "100.jpg", "104.jpg", "106.jpg", "116.jpg", "119.jpg", "137.jpg", "140.jpg", "148.jpg", "149.jpg", "150.jpg", "154.jpg", "156.jpg", "157.jpg", "158.jpg", "159.jpg", "161.jpg", "162.jpg", "164.jpg", "165.jpg", "166.jpg", "167.jpg", "168.jpg", "169.jpg", "170.jpg", "171.jpg", "172.jpg", "173.jpg", "174.jpg", "175.jpg", "176.jpg", "178.jpg", "179.jpg", "180.jpg", "181.jpg", "182.jpg", "183.jpg", "186.jpg", "189.jpg", "190.jpg", "192.jpg", "193.jpg", "194.jpg", "197.jpg", "198.jpg", "200.jpg", "201.jpg", "202.jpg", "203.jpg", "205.jpg", "206.jpg", "207.jpg", "208.jpg", "210.jpg", "213.jpg", "214.jpg", "215.jpg", "216.jpg", "218.jpg", "219.jpg", "220.jpg", "221.jpg", "222.jpg", "223.jpg", "224.jpg", "227.jpg", "0.png", "1.png", "2.png", "3.png", "5.png", "6.png", "9.png", "10.png", "11.png", "12.png", "14.png", "16.png", "19.png", "20.png", "21.png", "22.png", "23.png", "24.png", "26.png", "27.png", "28.png", "29.png", "30.png", "31.png", "32.png", "33.png", "34.png", "37.png", "39.png", "40.png", "41.png", "42.png", "43.png", "44.png", "45.png", "48.png", "49.png", "50.png", "51.png", "52.png", "53.png", "57.png", "58.png", "59.png", "64.png", "66.png", "67.png", "68.png", "69.png", "70.png", "71.png", "72.png", "76.png", "78.png", "79.png", "81.png", "82.png", "85.png", "86.png", "87.png", "89.png", "95.png", "98.png", "100.png", "101.png", "102.png", "105.png", "106.png", "107.png", "109.png", "110.png", "111.png", "112.png", "113.png", "114.png", "115.png", "116.png", "118.png", "119.png", "120.png", "121.png", "122.png", "123.png", "126.png", "128.png", "130.png", "134.png", "136.png", "138.png", "139.png", "140.png", "142.png", "145.png", "146.png", "149.png", "150.png", "151.png", "152.png", "153.png", "154.png", "155.png", "156.png", "157.png", "158.png", "159.png", "160.png", "163.png", "164.png", "165.png", "166.png", "167.png", "168.png", "169.png", "170.png", "171.png", "172.png", "173.png", "174.png", "178.png", "179.png", "180.png", "181.png", "182.png", "184.png", "186.png", "188.png", "190.png", "192.png", "193.png", "194.png", "195.png", "196.png", "197.png", "198.png", "200.png", "202.png", "203.png", "205.png", "206.png", "207.png", "209.png", "212.png", "213.png", "214.png", "216.png", "217.png", "218.png", "219.png", "220.png", "221.png", "222.png", "223.png", "224.png", "225.png", "226.png", "229.png", "231.png", "232.png", "233.png", "234.png", "235.png", "237.png", "238.png", "239.png", "240.png", "241.png", "242.png", "244.png", "245.png", "246.png", "247.png", "248.png", "249.png", "250.png", "253.png", "254.png", "255.png", "256.png", "257.png", "258.png", "259.png", "260.png", "262.png", "268.png", "0.gif", "1.gif", "2.gif", "3.gif", "4.gif", "5.gif", "6.gif", "7.gif", "8.gif", "9.gif", "10.gif", "12.gif", "13.gif", "14.gif", "15.gif", "16.gif", "18.gif", "19.gif", "20.gif", "21.gif", "22.gif", "23.gif", "24.gif", "28.gif", "29.gif", "30.gif", "33.gif", "34.gif", "35.gif", "36.gif", "37.gif", "39.gif", "40.gif", "42.gif", "44.gif", "45.gif", "46.gif", "48.gif", "50.gif", "52.gif", "54.gif", "55.gif", "57.gif", "58.gif", "59.gif", "60.gif", "61.gif", "63.gif", "64.gif", "66.gif", "67.gif", "68.gif", "69.gif", "70.gif", "72.gif", "73.gif", "75.gif", "76.gif", "77.gif", "78.gif", "80.gif", "81.gif", "82.gif", "83.gif", "86.gif", "87.gif", "88.gif", "92.gif", "93.gif", "94.gif", "95.gif", "96.gif", "97.gif", "98.gif", "99.gif", "100.gif", "101.gif", "102.gif", "103.gif", "104.gif", "105.gif", "106.gif", "108.gif", "109.gif", "110.gif", "111.gif", "112.gif", "113.gif", "115.gif", "116.gif", "117.gif", "118.gif", "119.gif", "120.gif", "122.gif", "123.gif", "124.gif", "127.gif", "129.gif", "130.gif", "131.gif", "134.gif", "135.gif", "136.gif", "138.gif", "139.gif", "141.gif", "144.gif", "146.gif", "148.gif", "149.gif", "153.gif", "154.gif", "155.gif", "157.gif", "158.gif", "159.gif", "160.gif", "161.gif", "162.gif", "164.gif", "166.gif", "167.gif", "168.gif", "169.gif", "170.gif", "171.gif", "172.gif", "173.gif", "174.gif", "175.gif", "176.gif", "177.gif", "178.gif", "181.gif", "182.gif", "183.gif", "185.gif", "186.gif", "187.gif", "188.gif", "189.gif", "190.gif", "191.gif", "192.gif", "193.gif", "195.gif", "196.gif", "197.gif", "200.gif", "201.gif", "202.gif", "203.gif", "204.gif", "205.gif", "206.gif", "207.gif", "208.gif", "209.gif", "210.gif", "211.gif", "212.gif", "213.gif", "214.gif", "215.gif", "216.gif", "217.gif", "219.gif", "220.gif", "221.gif", "222.gif", "224.gif", "225.gif", "226.gif", "227.gif", "228.gif", "230.gif", "232.gif", "233.gif", "234.gif", "235.gif", "238.gif", "240.gif", "241.gif", "243.gif", "244.gif", "245.gif", "246.gif", "247.gif", "249.gif", "250.gif", "251.gif", "253.gif"]; -export default banners; diff --git a/src/config/banners.json b/src/config/banners.json new file mode 100644 index 0000000000..785cc83064 --- /dev/null +++ b/src/config/banners.json @@ -0,0 +1 @@ +["0.jpg", "1.jpg", "2.jpg", "4.jpg", "6.jpg", "7.jpg", "8.jpg", "9.jpg", "10.jpg", "11.jpg", "12.jpg", "13.jpg", "14.jpg", "16.jpg", "17.jpg", "18.jpg", "19.jpg", "20.jpg", "21.jpg", "22.jpg", "24.jpg", "25.jpg", "26.jpg", "28.jpg", "29.jpg", "33.jpg", "38.jpg", "39.jpg", "43.jpg", "44.jpg", "45.jpg", "46.jpg", "47.jpg", "52.jpg", "54.jpg", "57.jpg", "59.jpg", "60.jpg", "61.jpg", "64.jpg", "66.jpg", "67.jpg", "69.jpg", "71.jpg", "72.jpg", "76.jpg", "77.jpg", "81.jpg", "82.jpg", "83.jpg", "84.jpg", "88.jpg", "90.jpg", "91.jpg", "96.jpg", "98.jpg", "99.jpg", "100.jpg", "104.jpg", "106.jpg", "116.jpg", "119.jpg", "137.jpg", "140.jpg", "148.jpg", "149.jpg", "150.jpg", "154.jpg", "156.jpg", "157.jpg", "158.jpg", "159.jpg", "161.jpg", "162.jpg", "164.jpg", "165.jpg", "166.jpg", "167.jpg", "168.jpg", "169.jpg", "170.jpg", "171.jpg", "172.jpg", "173.jpg", "174.jpg", "175.jpg", "176.jpg", "178.jpg", "179.jpg", "180.jpg", "181.jpg", "182.jpg", "183.jpg", "186.jpg", "189.jpg", "190.jpg", "192.jpg", "193.jpg", "194.jpg", "197.jpg", "198.jpg", "200.jpg", "201.jpg", "202.jpg", "203.jpg", "205.jpg", "206.jpg", "207.jpg", "208.jpg", "210.jpg", "213.jpg", "214.jpg", "215.jpg", "216.jpg", "218.jpg", "219.jpg", "220.jpg", "221.jpg", "222.jpg", "223.jpg", "224.jpg", "227.jpg", "0.png", "1.png", "2.png", "3.png", "5.png", "6.png", "9.png", "10.png", "11.png", "12.png", "14.png", "16.png", "19.png", "20.png", "21.png", "22.png", "23.png", "24.png", "26.png", "27.png", "28.png", "29.png", "30.png", "31.png", "32.png", "33.png", "34.png", "37.png", "39.png", "40.png", "41.png", "42.png", "43.png", "44.png", "45.png", "48.png", "49.png", "50.png", "51.png", "52.png", "53.png", "57.png", "58.png", "59.png", "64.png", "66.png", "67.png", "68.png", "69.png", "70.png", "71.png", "72.png", "76.png", "78.png", "79.png", "81.png", "82.png", "85.png", "86.png", "87.png", "89.png", "95.png", "98.png", "100.png", "101.png", "102.png", "105.png", "106.png", "107.png", "109.png", "110.png", "111.png", "112.png", "113.png", "114.png", "115.png", "116.png", "118.png", "119.png", "120.png", "121.png", "122.png", "123.png", "126.png", "128.png", "130.png", "134.png", "136.png", "138.png", "139.png", "140.png", "142.png", "145.png", "146.png", "149.png", "150.png", "151.png", "152.png", "153.png", "154.png", "155.png", "156.png", "157.png", "158.png", "159.png", "160.png", "163.png", "164.png", "165.png", "166.png", "167.png", "168.png", "169.png", "170.png", "171.png", "172.png", "173.png", "174.png", "178.png", "179.png", "180.png", "181.png", "182.png", "184.png", "186.png", "188.png", "190.png", "192.png", "193.png", "194.png", "195.png", "196.png", "197.png", "198.png", "200.png", "202.png", "203.png", "205.png", "206.png", "207.png", "209.png", "212.png", "213.png", "214.png", "216.png", "217.png", "218.png", "219.png", "220.png", "221.png", "222.png", "223.png", "224.png", "225.png", "226.png", "229.png", "231.png", "232.png", "233.png", "234.png", "235.png", "237.png", "238.png", "239.png", "240.png", "241.png", "242.png", "244.png", "245.png", "246.png", "247.png", "248.png", "249.png", "250.png", "253.png", "254.png", "255.png", "256.png", "257.png", "258.png", "259.png", "260.png", "262.png", "268.png", "0.gif", "1.gif", "2.gif", "3.gif", "4.gif", "5.gif", "6.gif", "7.gif", "8.gif", "9.gif", "10.gif", "12.gif", "13.gif", "14.gif", "15.gif", "16.gif", "18.gif", "19.gif", "20.gif", "21.gif", "22.gif", "23.gif", "24.gif", "28.gif", "29.gif", "30.gif", "33.gif", "34.gif", "35.gif", "36.gif", "37.gif", "39.gif", "40.gif", "42.gif", "44.gif", "45.gif", "46.gif", "48.gif", "50.gif", "52.gif", "54.gif", "55.gif", "57.gif", "58.gif", "59.gif", "60.gif", "61.gif", "63.gif", "64.gif", "66.gif", "67.gif", "68.gif", "69.gif", "70.gif", "72.gif", "73.gif", "75.gif", "76.gif", "77.gif", "78.gif", "80.gif", "81.gif", "82.gif", "83.gif", "86.gif", "87.gif", "88.gif", "92.gif", "93.gif", "94.gif", "95.gif", "96.gif", "97.gif", "98.gif", "99.gif", "100.gif", "101.gif", "102.gif", "103.gif", "104.gif", "105.gif", "106.gif", "108.gif", "109.gif", "110.gif", "111.gif", "112.gif", "113.gif", "115.gif", "116.gif", "117.gif", "118.gif", "119.gif", "120.gif", "122.gif", "123.gif", "124.gif", "127.gif", "129.gif", "130.gif", "131.gif", "134.gif", "135.gif", "136.gif", "138.gif", "139.gif", "141.gif", "144.gif", "146.gif", "148.gif", "149.gif", "153.gif", "154.gif", "155.gif", "157.gif", "158.gif", "159.gif", "160.gif", "161.gif", "162.gif", "164.gif", "166.gif", "167.gif", "168.gif", "169.gif", "170.gif", "171.gif", "172.gif", "173.gif", "174.gif", "175.gif", "176.gif", "177.gif", "178.gif", "181.gif", "182.gif", "183.gif", "185.gif", "186.gif", "187.gif", "188.gif", "189.gif", "190.gif", "191.gif", "192.gif", "193.gif", "195.gif", "196.gif", "197.gif", "200.gif", "201.gif", "202.gif", "203.gif", "204.gif", "205.gif", "206.gif", "207.gif", "208.gif", "209.gif", "210.gif", "211.gif", "212.gif", "213.gif", "214.gif", "215.gif", "216.gif", "217.gif", "219.gif", "220.gif", "221.gif", "222.gif", "224.gif", "225.gif", "226.gif", "227.gif", "228.gif", "230.gif", "232.gif", "233.gif", "234.gif", "235.gif", "238.gif", "240.gif", "241.gif", "243.gif", "244.gif", "245.gif", "246.gif", "247.gif", "249.gif", "250.gif", "251.gif", "253.gif"] \ No newline at end of file diff --git a/src/css/CSS.js b/src/css/CSS.js deleted file mode 100644 index 8d106a0892..0000000000 --- a/src/css/CSS.js +++ /dev/null @@ -1,37 +0,0 @@ -<% - var inc = require['style']; - var faCSS = read('/node_modules/font-awesome/css/font-awesome.css'); - var faWebFont = readBase64('/node_modules/font-awesome/fonts/fontawesome-webfont.woff'); - var mainCSS = ['font-awesome', 'style', 'yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon', 'spooky'].map(x => read(`${x}.css`)).join(''); - var iconNames = files.filter(f => /^linkify\.[^.]+\.png$/.test(f)); - var icons = iconNames.map(readBase64); -%>CSS = { - -boards: -<%= multiline( - inc.fa(faCSS, faWebFont) + mainCSS + inc.icons(iconNames, icons) + read('supports.css') -) %>, - -report: -<%= multiline(read('report.css')) %>, - -www: -<%= multiline(read('www.css')) %>, - -sub: function(css) { - var variables = { - site: g.SITE.selectors - }; - return css.replace(/\$[\w\$]+/g, function(name) { - var words = name.slice(1).split('$'); - var sel = variables; - for (var i = 0; i < words.length; i++) { - if (typeof sel !== 'object') return ':not(*)'; - sel = $.getOwn(sel, words[i]); - } - if (typeof sel !== 'string') return ':not(*)'; - return sel; - }); -} - -}; diff --git a/src/css/CSS.ts b/src/css/CSS.ts new file mode 100644 index 0000000000..7d9bf94c0a --- /dev/null +++ b/src/css/CSS.ts @@ -0,0 +1,107 @@ +// cSpell:ignore installGentoo, fontawesome, webfont + +import $ from '../platform/$'; + +// import boardCss from './board.css'; +import faCSS from '../../node_modules/font-awesome/css/font-awesome.css'; +import faWebFont from '../../node_modules/font-awesome/fonts/fontawesome-webfont.woff'; + +import burichan from './burichan.css'; +import fontAwesome from './font-awesome.css'; +import futaba from './futaba.css'; +import linkifyAudio from './linkify.audio.png'; +import linkifyBitchute from './linkify.bitchute.png'; +import linkifyClyp from './linkify.clyp.png'; +import linkifyDailymotion from './linkify.dailymotion.png'; +import linkifyGfycat from './linkify.gfycat.png'; +import linkifyGist from './linkify.gist.png'; +import linkifyImage from './linkify.image.png'; +import linkifyInstallgentoo from './linkify.installgentoo.png'; +import linkifyLiveleak from './linkify.liveleak.png'; +import linkifyPastebin from './linkify.pastebin.png'; +import linkifyPeertube from './linkify.peertube.png'; +import linkifySoundcloud from './linkify.soundcloud.png'; +import linkifyStreamable from './linkify.streamable.png'; +import linkifyTwitchtv from './linkify.twitchtv.png'; +import linkifyTwitter from './linkify.twitter.png'; +import linkifyVideo from './linkify.video.png'; +import linkifyVidlii from './linkify.vidlii.png'; +import linkifyCimeo from './linkify.vimeo.png'; +import linkifyVine from './linkify.vine.png'; +import linkifyVocaroo from './linkify.vocaroo.png'; +import linkifyYoutube from './linkify.youtube.png'; + +import photon from './photon.css'; +import report from './report.css'; +import spooky from './spooky.css'; +import style from './style.css'; +// style.inc +import supports from './supports.css'; +import tomorrow from './tomorrow.css'; +import www from './www.css'; +import yotsubaB from './yotsuba-b.css'; +import yotsuba from './yotsuba.css'; +import { fa, icons } from './style'; +import { g } from '../globals/globals'; + +// <% + // var inc = require['style']; + // var faCSS = read('/node_modules/font-awesome/css/font-awesome.css'); + // var faWebFont = readBase64('/node_modules/font-awesome/fonts/fontawesome-webfont.woff'); + // var mainCSS = ['font-awesome', 'style', 'yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon', 'spooky'].map(x => read(`${x}.css`)).join(''); +// var iconNames = files.filter(f => /^linkify\.[^.]+\.png$/.test(f)); +// var icons = iconNames.map(readBase64); +// %> + +const mainCSS = fontAwesome + style + yotsuba +yotsubaB+futaba+burichan+tomorrow + photon + spooky; +const faIcons: { name: string, data: string }[] = [ + { name: "Audio", data: linkifyAudio }, + { name: "Bitchute", data: linkifyBitchute }, + { name: "Clyp", data: linkifyClyp }, + { name: "Dailymotion", data: linkifyDailymotion }, + { name: "Gfycat", data: linkifyGfycat }, + { name: "Gist", data: linkifyGist }, + { name: "Image", data: linkifyImage }, + { name: "Installgentoo", data: linkifyInstallgentoo }, + { name: "Liveleak", data: linkifyLiveleak }, + { name: "Pastebin", data: linkifyPastebin }, + { name: "Peertube", data: linkifyPeertube }, + { name: "Soundcloud", data: linkifySoundcloud }, + { name: "Streamable", data: linkifyStreamable }, + { name: "Twitchtv", data: linkifyTwitchtv }, + { name: "Twitter", data: linkifyTwitter }, + { name: "Video", data: linkifyVideo }, + { name: "Vidlii", data: linkifyVidlii }, + { name: "Cimeo", data: linkifyCimeo }, + { name: "Vine", data: linkifyVine }, + { name: "Vocaroo", data: linkifyVocaroo }, + { name: "Youtube", data: linkifyYoutube }, +]; + +const CSS = { + + boards: fa(faCSS, faWebFont) + mainCSS + icons(faIcons) + supports, + + report, + + www, + + sub: function(css: string) { + var variables = { + site: g.SITE.selectors + }; + return css.replace(/\$[\w\$]+/g, function(name) { + var words = name.slice(1).split('$'); + var sel = variables; + for (var i = 0; i < words.length; i++) { + if (typeof sel !== 'object') return ':not(*)'; + sel = $.getOwn(sel, words[i]); + } + if (typeof sel !== 'string') return ':not(*)'; + return sel; + }); + } + +}; + +export default CSS; diff --git a/src/css/style.inc b/src/css/style.inc deleted file mode 100644 index 8eb7dae906..0000000000 --- a/src/css/style.inc +++ /dev/null @@ -1,41 +0,0 @@ -/* jshint esnext: true */ - -// == Reprocess Font Awesome CSS == // -var fa = (css, font) => ( - -// Font Awesome CSS attribution and license -css.match(/\/\*\![^]*?\*\//)[0] + '\n' + - -// Font Awesome web font -`@font-face { - font-family: FontAwesome; - src: url('data:application/font-woff;base64,${font}') format('woff'); - font-weight: 400; - font-style: normal; -} -` + - -// fa-[icon name] classes -css - .match(/(\.fa-[^{]*{\s*content:[^}]*}\s*)+/)[0] - .replace(/([,{;])\s+/g, '$1') - .replace(/,/g, ', ') - -); - -// == Create CSS for Link Title Favicons == // -var icons = (names, data) => ( - -'/* Link Title Favicons */\n' + -names.map((file, i) => -`.linkify.${file.split('.')[1]}::before { - content: ""; - background: transparent url('data:image/png;base64,${data[i]}') center left no-repeat!important; - padding-left: 18px; -} -` -).join('') - -); - -return {fa, icons}; diff --git a/src/css/style.ts b/src/css/style.ts new file mode 100644 index 0000000000..f13f37f8e7 --- /dev/null +++ b/src/css/style.ts @@ -0,0 +1,37 @@ +// == Reprocess Font Awesome CSS == // +export const fa = (css: string, font: string) => ( + + // Font Awesome CSS attribution and license + css.match(/\/\*\![^]*?\*\//)[0] + '\n' + + + // Font Awesome web font + `@font-face { + font-family: FontAwesome; + src: url('data:application/font-woff;base64,${font}') format('woff'); + font-weight: 400; + font-style: normal; +} +` + + + // fa-[icon name] classes + css + .match(/(\.fa-[^{]*{\s*content:[^}]*}\s*)+/)[0] + .replace(/([,{;])\s+/g, '$1') + .replace(/,/g, ', ') + +); + +// == Create CSS for Link Title Favicons == // +export const icons = (data: { name: string, data: string }[]) => ( + + '/* Link Title Favicons */\n' + + data.map(({ name, data }) => + `.linkify.${name}::before { + content: ""; + background: transparent url('data:image/png;base64,${data}') center left no-repeat!important; + padding-left: 18px; +} +` + ).join('') + +); diff --git a/src/globals/globals.js b/src/globals/globals.js deleted file mode 100644 index 6b3f2b47f8..0000000000 --- a/src/globals/globals.js +++ /dev/null @@ -1,46 +0,0 @@ -var Conf, E, c, d, doc, docSet, g; - -Conf = Object.create(null); -c = console; -d = document; -doc = d.documentElement; - -// Workaround for userscript managers that run script before document.documentElement is set -docSet = function() { - return (doc = d.documentElement); -}; - -g = { - VERSION: '<%= readJSON('/version.json').version %>', - NAMESPACE: '<%= meta.name %>.', - sites: Object.create(null), - boards: Object.create(null) -}; - -E = (function() { - var fn, r, regex, str; - str = { - '&': '&', - "'": ''', - '"': '"', - '<': '<', - '>': '>' - }; - r = String.prototype.replace; - regex = /[&"'<>]/g; - fn = function(x) { - return str[x]; - }; - return function(text) { - return r.call(text, regex, fn); - }; -})(); - -E.cat = function(templates) { - var html, i, len; - html = ''; - for (i = 0, len = templates.length; i < len; i++) { - html += templates[i].innerHTML; - } - return html; -}; diff --git a/src/globals/globals.ts b/src/globals/globals.ts new file mode 100644 index 0000000000..ac52c9a31b --- /dev/null +++ b/src/globals/globals.ts @@ -0,0 +1,93 @@ +import version from "../../version.json"; +import meta from "../../package.json"; +import type SimpleDict from "../classes/SimpleDict"; +import type Post from "../classes/Post"; +import type Thread from "../classes/Thread"; +import type SWTinyboard from "../site/SW.tinyboard"; + +// interfaces might be incomplete +export interface BoardConfig { + board: string + bump_limit: number + cooldowns: { + threads: number, + replies: number, + images: number, + } + custom_spoilers: 1 | 0, + image_limit: number, + is_archived: 1 | 0, + max_comment_chars: number + max_filesize: number + max_webm_duration: number + max_webm_filesize: number + meta_description: string, + pages: number, + per_page: number, + spoilers: number, + title: string + ws_board: 1 | 0 +} + +export interface Board { + ID: string, + boardID: string, + siteID: string, + config: BoardConfig, + posts: SimpleDict, + threads: SimpleDict, +} + +export const Conf = Object.create(null); + +export const g: { + VERSION: string, + NAMESPACE: string, + sites: (typeof SWTinyboard)[], + boards: Board[], + posts?: SimpleDict, + threads?: SimpleDict + THREADID?: number, + SITE?: typeof SWTinyboard, + BOARD?: Board, + VIEW?: string, +} = { + VERSION: version.version, + NAMESPACE: meta.name, + sites: Object.create(null), + boards: Object.create(null) +}; + +export const E = (function () { + const str = { + '&': '&', + "'": ''', + '"': '"', + '<': '<', + '>': '>' + }; + const regex = /[&"'<>]/g; + const fn = function (x: string) { + return str[x]; + }; + const output = function (text: string) { + return text.toString().replace(regex, fn); + }; + output.cat = function (templates) { + let html = ''; + for (let i = 0; i < templates.length; i++) { + html += templates[i].innerHTML; + } + return html; + }; + return output; +})(); + +export const d = document; +export const doc = d.documentElement; + +export const c = console; + +export const docSet = function () { + // return (doc = d.documentElement); +}; diff --git a/src/globals/jsx.ts b/src/globals/jsx.ts new file mode 100644 index 0000000000..9556753ca4 --- /dev/null +++ b/src/globals/jsx.ts @@ -0,0 +1,70 @@ +/* + * This file has the code for the jsx to { innerHTML: "safe string" } + * + * Usage: import h from this file. + * Attributes are stringified raw, so the names must be like html text: eg class and not className. + * Boolean values are stringified as followed: true will mean the attribute is there, false means it will be omitted. + * Strings bound to attributes and children will be escaped automatically. + * It returns interface EscapedHtml { innerHTML: "safe string", [isEscaped]: true } + * + * For strings that don't have a parent element you can use fragments: <>. + * Note that you need to import hFragment, which for some reason isn't auto imported on "add all missing imports" + */ + +import { E } from "./globals"; + +/** + * The symbol indicating that a string is safely escaped. + * This is a symbol so it can't be faked by a json blob from the internet. + */ +export const isEscaped = Symbol('isEscaped'); + +export interface EscapedHtml { + innerHTML: string, + [isEscaped]: true, +} + +const voidElements = new Set( + ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'source', 'track', 'wbr',] +); + +export const hFragment = Symbol('hFragment'); + +/** Function that jsx/tsx will be compiled to. */ +export default function h( + tag: string | typeof hFragment, + attributes: Record | null, + ...children: unknown[] +): EscapedHtml { + let innerHTML = tag === hFragment ? '' : `<${tag}`; + + if (attributes) { + for (const [attribute, value] of Object.entries(attributes)) { + if (!value && value !== 0) continue; + innerHTML += ` ${attribute}`; + if (value === true) continue; + innerHTML += `="${E(value.toString())}"`; + } + } + if (tag !== hFragment) innerHTML += '>'; + + const isVoid = tag !== hFragment && voidElements.has(tag); + if (isVoid) { + if (children.length) throw new TypeError(`${tag} is a void html element and can't have child elements`); + } else { + for (const child of children) { + if (child === null || child === undefined || child === '') continue; + + if (child instanceof Object && "innerHTML" in child && child[isEscaped]) { + innerHTML += child.innerHTML; + continue; + } + + innerHTML += E(child.toString()); + } + } + + if (!isVoid && tag !== hFragment) innerHTML += ``; + + return { innerHTML, [isEscaped]: true }; +} diff --git a/src/main/Main.js b/src/main/Main.js index fdd8aa11f7..e7edc940d7 100644 --- a/src/main/Main.js +++ b/src/main/Main.js @@ -1,3 +1,97 @@ +import Redirect from "../Archive/Redirect"; +import Board from "../classes/Board"; +import Callbacks from "../classes/Callbacks"; +import CatalogThreadNative from "../classes/CatalogThreadNative"; +import DataBoard from "../classes/DataBoard"; +import Notice from "../classes/Notice"; +import Post from "../classes/Post"; +import SimpleDict from "../classes/SimpleDict"; +import Thread from "../classes/Thread"; +import Config from "../config/Config"; +import Anonymize from "../Filtering/Anonymize"; +import Filter from "../Filtering/Filter"; +import PostHiding from "../Filtering/PostHiding"; +import Recursive from "../Filtering/Recursive"; +import ThreadHiding from "../Filtering/ThreadHiding"; +import Index from "../General/Index"; +import Settings from "../General/Settings"; +import FappeTyme from "../Images/FappeTyme"; +import Gallery from "../Images/Gallery"; +import ImageCommon from "../Images/ImageCommon"; +import ImageExpand from "../Images/ImageExpand"; +import ImageHost from "../Images/ImageHost"; +import ImageHover from "../Images/ImageHover"; +import ImageLoader from "../Images/ImageLoader"; +import Metadata from "../Images/Metadata"; +import RevealSpoilers from "../Images/RevealSpoilers"; +import Sauce from "../Images/Sauce"; +import Volume from "../Images/Volume"; +import Linkify from "../Linkification/Linkify"; +import ArchiveLink from "../Menu/ArchiveLink"; +import CopyTextLink from "../Menu/CopyTextLink"; +import DeleteLink from "../Menu/DeleteLink"; +import DownloadLink from "../Menu/DownloadLink"; +import ReportLink from "../Menu/ReportLink"; +import AntiAutoplay from "../Miscellaneous/AntiAutoplay"; +import Banner from "../Miscellaneous/Banner"; +import CatalogLinks from "../Miscellaneous/CatalogLinks"; +import CustomCSS from "../Miscellaneous/CustomCSS"; +import ExpandComment from "../Miscellaneous/ExpandComment"; +import ExpandThread from "../Miscellaneous/ExpandThread"; +import FileInfo from "../Miscellaneous/FileInfo"; +import Flash from "../Miscellaneous/Flash"; +import Fourchan from "../Miscellaneous/Fourchan"; +import IDColor from "../Miscellaneous/IDColor"; +import IDHighlight from "../Miscellaneous/IDHighlight"; +import IDPostCount from "../Miscellaneous/IDPostCount"; +import Keybinds from "../Miscellaneous/Keybinds"; +import ModContact from "../Miscellaneous/ModContact"; +import Nav from "../Miscellaneous/Nav"; +import NormalizeURL from "../Miscellaneous/NormalizeURL"; +import PostJumper from "../Miscellaneous/PostJumper"; +import PSA from "../Miscellaneous/PSA"; +import PSAHiding from "../Miscellaneous/PSAHiding"; +import RelativeDates from "../Miscellaneous/RelativeDates"; +import RemoveSpoilers from "../Miscellaneous/RemoveSpoilers"; +import ThreadLinks from "../Miscellaneous/ThreadLinks"; +import Time from "../Miscellaneous/Time"; +import Tinyboard from "../Miscellaneous/Tinyboard"; +import Favicon from "../Monitoring/Favicon"; +import MarkNewIPs from "../Monitoring/MarkNewIPs"; +import ReplyPruning from "../Monitoring/ReplyPruning"; +import ThreadStats from "../Monitoring/ThreadStats"; +import ThreadUpdater from "../Monitoring/ThreadUpdater"; +import ThreadWatcher from "../Monitoring/ThreadWatcher"; +import Unread from "../Monitoring/Unread"; +import UnreadIndex from "../Monitoring/UnreadIndex"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import PassLink from "../Posting/PassLink"; +import PostRedirect from "../Posting/PostRedirect"; +import QR from "../Posting/QR"; +import QuoteBacklink from "../Quotelinks/QuoteBacklink"; +import QuoteCT from "../Quotelinks/QuoteCT"; +import QuoteInline from "../Quotelinks/QuoteInline"; +import QuoteOP from "../Quotelinks/QuoteOP"; +import QuotePreview from "../Quotelinks/QuotePreview"; +import QuoteStrikeThrough from "../Quotelinks/QuoteStrikeThrough"; +import QuoteThreading from "../Quotelinks/QuoteThreading"; +import QuoteYou from "../Quotelinks/QuoteYou"; +import Quotify from "../Quotelinks/Quotify"; +import Site from "../site/Site"; +import SW from "../site/SW"; +import CSS from "../css/CSS"; +import meta from '../../package.json'; +import Header from "../General/Header"; +import { c, Conf, d, doc, docSet, g } from "../globals/globals"; +import Menu from "../Menu/Menu"; +import BoardConfig from "../General/BoardConfig"; +import CaptchaReplace from "../Posting/Captcha.replace"; +import Get from "../General/Get"; +import Captcha from "../Posting/Captcha"; +import { dict, platform } from "../platform/helpers"; +// import Test from "../General/Test"; + /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -14,9 +108,9 @@ var Main = { let key; try { let w = window; - if ($.platform === 'crx') { w = (w.wrappedJSObject || w); } - if ('<%= meta.name %> antidup' in w) { return; } - w['<%= meta.name %> antidup'] = true; + if (platform === 'crx') { w = (w.wrappedJSObject || w); } + if (`${meta.name} antidup` in w) { return; } + w[`${meta.name} antidup`] = true; } catch (error) {} // Don't run inside ad iframes. @@ -53,7 +147,7 @@ var Main = { // Flatten default values from Config into Conf var flatten = function(parent, obj) { if (obj instanceof Array) { - Conf[parent] = $.dict.clone(obj[0]); + Conf[parent] = dict.clone(obj[0]); } else if (typeof obj === 'object') { for (var key in obj) { var val = obj[key]; @@ -83,16 +177,16 @@ var Main = { flatten(null, Config); for (var db of DataBoard.keys) { - Conf[db] = $.dict(); + Conf[db] = dict(); } - Conf['customTitles'] = $.dict.clone({'4chan.org': {boards: {'qa': {'boardTitle': {orig: '/qa/ - Question & Answer', title: '/qa/ - 2D/Random'}}}}}); - Conf['boardConfig'] = {boards: $.dict()}; + Conf['customTitles'] = dict.clone({'4chan.org': {boards: {'qa': {'boardTitle': {orig: '/qa/ - Question & Answer', title: '/qa/ - 2D/Random'}}}}}); + Conf['boardConfig'] = {boards: dict()}; Conf['archives'] = Redirect.archives; - Conf['selectedArchives'] = $.dict(); - Conf['cooldowns'] = $.dict(); - Conf['Index Sort'] = $.dict(); - for (let i = 0; i < 2; i++) { Conf[`Last Long Reply Thresholds ${i}`] = $.dict(); } - Conf['siteProperties'] = $.dict(); + Conf['selectedArchives'] = dict(); + Conf['cooldowns'] = dict(); + Conf['Index Sort'] = dict(); + for (let i = 0; i < 2; i++) { Conf[`Last Long Reply Thresholds ${i}`] = dict(); } + Conf['siteProperties'] = dict(); // XXX old key names Conf['Except Archives from Encryption'] = false; @@ -106,7 +200,7 @@ var Main = { Conf['Use Faster Image Host'] = 'true'; Conf['Captcha Fixes'] = true; Conf['captchaServiceDomain'] = ''; - Conf['captchaServiceKey'] = $.dict(); + Conf['captchaServiceKey'] = dict(); // Enforce JS whitelist if ( @@ -118,7 +212,7 @@ var Main = { } // Get saved values as items - const items = $.dict(); + const items = dict(); for (key in Conf) { items[key] = undefined; } items['previousversion'] = undefined; return ($.getSync || $.get)(items, function(items) { @@ -162,9 +256,8 @@ var Main = { items.previousversion = (changes.previousversion = g.VERSION); return $.set(changes, function() { if (items['Show Updated Notifications'] ?? true) { - // TODO meta const el = $.el('span', - { innerHTML: 'meta.name has been updated to version ${g.VERSION}.'}); + { innerHTML: `${meta.name} has been updated to version ${g.VERSION}.` }); return new Notice('info', el, 15); } }); @@ -670,7 +763,7 @@ var Main = { $.addClass(doc, 'tainted'); if (Conf['Disable Native Extension'] && !Main.isFirstRun) { const msg = $.el('div', - {innerHTML: 'Failed to disable the native extension. You may need to block it.'}); + { innerHTML: 'Failed to disable the native extension. You may need to block it.' }); new Notice('error', msg); } } @@ -686,9 +779,11 @@ var Main = { return; } - const div = $.el('div', - {innerHTML: '${errors.length} errors occurred.&{Main.reportLink(errors)} [show]'}); - $.on(div.lastElementChild, 'click', function() { + const div = $.el('div', { + innerHTML: + `${errors.length} errors occurred.${Main.reportLink(errors).innerHTML} [show]` + }); + $.on(div.lastElementChild, 'click', function () { let ref; return [this.textContent, logs.hidden] = Array.from(ref = this.textContent === 'show' ? ( ['hide', false] @@ -709,12 +804,12 @@ var Main = { parseError(data, reportLink) { c.error(data.message, data.error.stack); const message = $.el('div', - {innerHTML: '${data.message}?{reportLink}{&{reportLink}}'}); + { innerHTML: E(data.message) + ((reportLink) ? (reportLink).innerHTML : "") }); const error = $.el('div', {textContent: `${data.error.name || 'Error'}: ${data.error.message || 'see console for details'}`}); const lines = data.error.stack?.match(/\d+(?=:\d+\)?$)/mg)?.join().replace(/^/, ' at ') || ''; const context = $.el('div', - {textContent: `(<%= meta.name %> <%= meta.fork %> v${g.VERSION} ${$.platform} on ${$.engine}${lines})`}); + { textContent: `(${meta.name} ${meta.fork} v${g.VERSION} ${platform} on ${$.engine}${lines})` }); return [message, error, context]; }, @@ -725,20 +820,19 @@ var Main = { if (errors.length > 1) { title += ` (+${errors.length - 1} other errors)`; } let details = ''; const addDetails = function(text) { - // TODO meta - if (encodeURIComponent(title + details + text + '\n').length <= "meta.newIssueMaxLength - meta.newIssue.replace(/%(title|details)/, '')".length) { + if (encodeURIComponent(title + details + text + '\n').length <= meta.newIssueMaxLength - meta.newIssue.replace(/%(title|details)/, '').length) { return details += text + '\n'; } }; addDetails(`\ [Please describe the steps needed to reproduce this error.] -Script: <%= meta.name %> <%= meta.fork %> v${g.VERSION} ${$.platform} +Script: ${meta.name} ${meta.fork} v${g.VERSION} ${platform} URL: ${location.href} User agent: ${navigator.userAgent}\ ` ); - if (($.platform === 'userscript') && (info = (() => { + if ((platform === 'userscript') && (info = (() => { if (typeof GM !== 'undefined' && GM !== null) { return GM.info; } else { if (typeof GM_info !== 'undefined' && GM_info !== null) { return GM_info; } } })())) { @@ -748,8 +842,8 @@ User agent: ${navigator.userAgent}\ if (data.error.stack) { addDetails(data.error.stack.replace(data.error.toString(), '').trim()); } if (data.html) { addDetails('\n`' + data.html + '`'); } details = details.replace(/file:\/{3}.+\//g, ''); // Remove local file paths - const url = '<%= meta.newIssue %>'.replace('%title', encodeURIComponent(title)).replace('%details', encodeURIComponent(details)); - return {innerHTML: ' [report]'}; + const url = meta.newIssue.replace('%title', encodeURIComponent(title)).replace('%details', encodeURIComponent(details)); + return { innerHTML: ` [report]` }; }, isThisPageLegit() { @@ -865,7 +959,9 @@ User agent: ${navigator.userAgent}\ ['Mod Contact Links', ModContact] ] }; +export default Main; +$.ready(() => Main.init()); // <% if (readJSON('/.tests_enabled')) { %> -// Main.features.push ['Build Test', Test] +// Main.features.push(['Build Test', Test]); // <% } %> diff --git a/src/meta/manifest.json b/src/meta/manifest.json deleted file mode 100644 index 1c82e9c8e0..0000000000 --- a/src/meta/manifest.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "<%= meta.name %>", - "version": "<%= readJSON('/version.json').version %>", - "manifest_version": 2, - "description": "<%= description %>", - "icons": { - "16": "icon16.png", - "48": "icon48.png", - "128": "icon128.png" - }, - "content_scripts": [{ - "js": ["script.js"], - "matches": <%= JSON.stringify(meta.matches_only.concat(meta.matches, meta.matches_extra)) %>, - "exclude_matches": <%= JSON.stringify(meta.exclude_matches) %>, - "all_frames": true, - "run_at": "document_start" - }], - "background": { - "scripts": ["eventPage.js"], - "persistent": false - }, - "homepage_url": "<%= meta.page %>", -<% if (channel !== '-noupdate') { %> "update_url": "<%= meta.downloads %>updates<%= channel %>.xml", - "key": "<%= meta.appid %>", -<% } %> "minimum_chrome_version": "<%= meta.min.chrome %>", - "permissions": <%= JSON.stringify(meta.matches_only.concat(meta.matches, ["storage"])) %>, - "optional_permissions": [ - "*://*/" - ], - "applications": { - "gecko": { - "id": "<%= meta.appidGecko %>"<% if (channel !== '-noupdate') { %>, - "update_url": "<%= meta.downloads %>updates<%= channel %>.json" -<% } %> } - } -} diff --git a/src/meta/manifestJson.js b/src/meta/manifestJson.js new file mode 100644 index 0000000000..92cea7a96e --- /dev/null +++ b/src/meta/manifestJson.js @@ -0,0 +1,42 @@ +export default function generateManifestJson(p, version, channel) { + const manifest = { + "name": p.meta.name, + "version": version.version, + "manifest_version": 2, + "description": p.description, + "icons": { + "16": "icon16.png", + "48": "icon48.png", + "128": "icon128.png" + }, + "content_scripts": [{ + "js": ["script.js"], + "matches": p.meta.matches_only.concat(p.meta.matches, p.meta.matches_extra), + "exclude_matches": p.meta.exclude_matches, + "all_frames": true, + "run_at": "document_start" + }], + "background": { + "scripts": ["eventPage.js"], + "persistent": false + }, + "homepage_url": p.meta.page, + "minimum_chrome_version": p.meta.min.chrome, + "permissions": p.meta.matches_only.concat(p.meta.matches, ["storage"]), + "optional_permissions": [ + "*://*/" + ], + "applications": { + "gecko": { + "id": p.meta.appidGecko, + } + } + }; + + if (channel !== '-noupdate') { + manifest.update_url = `${p.meta.downloads}updates${channel}.xml`; + manifest.applications.gecko.update_url = `${p.meta.downloads}updates${channel}.json`; + } + + return JSON.stringify(manifest, undefined, 2); +} diff --git a/src/meta/metadata.js b/src/meta/metadata.js index f154139e15..061f7ec1d9 100644 --- a/src/meta/metadata.js +++ b/src/meta/metadata.js @@ -1,18 +1,38 @@ -// ==UserScript== -// @name <%= meta.name %><%= (channel === '-beta') ? ' beta' : '' %> -// @version <%= readJSON('/version.json').version %> -// @minGMVer <%= meta.min.greasemonkey %> -// @minFFVer <%= meta.min.firefox %> -// @namespace <%= name %> -// @description <%= description %> -// @license MIT; <%= meta.license %> -<%= - (function() { +// this file is needed in the build script, keep it .js + +import { readFile } from "fs/promises"; +import { dirname, resolve } from "path"; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default async function generateMetadata(packageJson, channel) { + const meta = packageJson.meta; + + const versionFile = await readFile(resolve(__dirname, '../../version.json')); + const version = JSON.parse(versionFile.toString()); + + const iconFile = await readFile(resolve(__dirname, './icon48.png')); + const icon = Buffer.from(iconFile).toString('base64'); + + const archives = JSON.parse(await readFile(resolve(__dirname, '../Archive/archives.json'), { encoding: 'utf-8' })); + + let output = `// ==UserScript== +// @name ${meta.name}${channel === '-beta' ? ' beta' : ''} +// @version ${version.version} +// @minGMVer ${meta.min.greasemonkey} +// @minFFVer ${meta.min.firefox} +// @namespace ${packageJson.name} +// @description ${packageJson.description} +// @license MIT; ${meta.license} +`; + + output += (function () { function expand(items, regex, substitutions) { var results = []; - items.forEach(function(item) { + items.forEach(function (item) { if (regex.test(item)) { - substitutions.forEach(function(s) { + substitutions.forEach(function (s) { results.push(item.replace(regex, s)); }); } else { @@ -25,24 +45,26 @@ return expand(matches, /^\*/, ['http', 'https']); } return [].concat( - expandMatches(meta.includes_only.concat(meta.matches, meta.matches_extra)).map(function(match) { + expandMatches(meta.includes_only.concat(meta.matches, meta.matches_extra)).map(function (match) { return '// @include ' + match; }), - expandMatches(meta.exclude_matches).map(function(match) { + expandMatches(meta.exclude_matches).map(function (match) { return '// @exclude ' + match; }) ).join('\n'); - })() -%> + })(); + + output += ` // @connect 4chan.org // @connect 4channel.org // @connect 4cdn.org // @connect 4chenz.github.io -<%= - readJSON('/src/Archive/archives.json').map(function(archive) { +`; + output += archives.map(function (archive) { return '// @connect ' + archive.domain; - }).join('\n') -%> + }).join('\n'); + + output += ` // @connect api.clyp.it // @connect api.dailymotion.com // @connect api.github.com @@ -51,13 +73,24 @@ // @connect vimeo.com // @connect www.youtube.com // @connect * -<%= - meta.grants.map(function(grant) { +`; + output += meta.grants.map(function (grant) { return '// @grant ' + grant; - }).join('\n') -%> -// @run-at document-start -// @updateURL <%= (channel !== '-noupdate') ? `${meta.downloads}${name}${channel}.meta.js` : 'https://noupdate.invalid/' %> -// @downloadURL <%= (channel !== '-noupdate') ? `${meta.downloads}${name}${channel}.user.js` : 'https://noupdate.invalid/' %> -// @icon data:image/png;base64,<%= readBase64('/src/meta/icon48.png') %> + }).join('\n'); + + output += '\n// @run-at document-start'; + + if (channel === '-noupdate') { + output += '\n// @updateURL https://noupdate.invalid/\n// @downloadURL https://noupdate.invalid/'; + } else { + output += ` +// @updateURL ${meta.downloads}${packageJson.name}${channel}.meta.js +// @downloadURL ${meta.downloads}${packageJson.name}${channel}.user.js`; + } + output += ` +// @icon data:image/png;base64,${icon} // ==/UserScript== +`; + + return output; +} diff --git a/src/meta/tsconfig.json b/src/meta/tsconfig.json new file mode 100644 index 0000000000..d37071c3e2 --- /dev/null +++ b/src/meta/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "moduleResolution": "node16" + }, + "extends": "../../package.json" +} \ No newline at end of file diff --git a/src/platform/$$.js b/src/platform/$$.js index 16fa613360..6f9d9a8253 100644 --- a/src/platform/$$.js +++ b/src/platform/$$.js @@ -4,4 +4,5 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -const $$ = (selector, root=d.body) => [...Array.from(root.querySelectorAll(selector))]; +const $$ = (selector, root = d.body) => [...Array.from(root.querySelectorAll(selector))]; +export default $$; diff --git a/src/platform/$$.ts b/src/platform/$$.ts new file mode 100644 index 0000000000..ef8ee67958 --- /dev/null +++ b/src/platform/$$.ts @@ -0,0 +1,8 @@ +const $$ = (selector: string, root?: HTMLElement) => { + if (!root) { + if (!document.body) return []; + root = document.body; + } + return Array.from(root.querySelectorAll(selector)); +}; +export default $$; diff --git a/src/platform/$.js b/src/platform/$.js index bbae05122f..bbdce18b68 100644 --- a/src/platform/$.js +++ b/src/platform/$.js @@ -7,16 +7,14 @@ */ // loosely follows the jquery api: // http://api.jquery.com/ -// not chainable -const $ = (selector, root=d.body) => root.querySelector(selector); -$.DAY = 24 * ( - $.HOUR = 60 * ( - $.MINUTE = 60 * ( - $.SECOND = 1000 - ) - ) -); +import Notice from "../classes/Notice"; +import { c, Conf, d, doc, g } from "../globals/globals"; +import CrossOrigin from "./CrossOrigin"; +import { debounce, dict, MINUTE, platform, SECOND } from "./helpers"; + +// not chainable +const $ = (selector, root = document.body) => root.querySelector(selector); $.id = id => d.getElementById(id); @@ -57,29 +55,6 @@ $.extend = function(object, properties) { } }; -$.dict = () => Object.create(null); - -$.dict.clone = function(obj) { - if ((typeof obj !== 'object') || (obj === null)) { - return obj; - } else if (obj instanceof Array) { - const arr = []; - for (let i = 0, end = obj.length; i < end; i++) { - arr.push($.dict.clone(obj[i])); - } - return arr; - } else { - const map = Object.create(null); - for (var key in obj) { - var val = obj[key]; - map[key] = $.dict.clone(val); - } - return map; - } -}; - -$.dict.json = str => $.dict.clone(JSON.parse(str)); - $.hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key); $.getOwn = function(obj, key) { @@ -94,16 +69,17 @@ $.ajax = (function() { pageXHR = XMLHttpRequest; } - (function(url, options={}) { + const r = (function (url, options={}) { if (options.responseType == null) { options.responseType = 'json'; } if (!options.type) { options.type = (options.form && 'post') || 'get'; } // XXX https://forums.lanik.us/viewtopic.php?f=64&t=24173&p=78310 url = url.replace(/^((?:https?:)?\/\/(?:\w+\.)?(?:4chan|4channel|4cdn)\.org)\/adv\//, '$1//adv/'); - // <% if (type === 'crx') { %> - // # XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638 - // if Conf['Work around CORB Bug'] and g.SITE.software is 'yotsuba' and !options.testCORB and FormData.prototype.entries - // return $.ajaxPage url, options - // <% } %> + if (platform === 'crx') { + // XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638 + if (Conf['Work around CORB Bug'] && g.SITE.software === 'yotsuba' && !options.testCORB && FormData.prototype.entries) { + return $.ajaxPage(url, options); + } + } const {onloadend, timeout, responseType, withCredentials, type, onprogress, form, headers} = options; const r = new pageXHR(); try { @@ -117,12 +93,14 @@ $.ajax = (function() { $.extend(r.upload, {onprogress}); // connection error or content blocker $.on(r, 'error', function() { if (!r.status) { return c.warn(`4chan X failed to load: ${url}`); } }); - // <% if (type === 'crx') { %> - // # XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638 - // $.on r, 'load', -> - // if !Conf['Work around CORB Bug'] and r.readyState is 4 and r.status is 200 and r.statusText is '' and r.response is null - // $.set 'Work around CORB Bug', (Conf['Work around CORB Bug'] = Date.now()) - // <% } %> + if (platform === 'crx') { + // https://bugs.chromium.org/p/chromium/issues/detail?id=920638 + $.on(r, 'load', () => { + if (!Conf['Work around CORB Bug'] && r.readyState === 4 && r.status === 200 && r.statusText === '' && r.response === null) { + $.set('Work around CORB Bug', (Conf['Work around CORB Bug'] = Date.now())); + } + }); + } r.send(form); } catch (err) { // XXX Some content blockers in Firefox (e.g. Adblock Plus and NoScript) throw an exception instead of simulating a connection error. @@ -134,58 +112,59 @@ $.ajax = (function() { return r; }); -// <% if (type === 'crx') { %> -// # XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638 -// do -> - let requestID = 0; - const requests = $.dict(); - - $.ajaxPageInit = function() { - $.global(function() { - window.FCX.requests = Object.create(null); - - document.addEventListener('4chanXAjax', function(e) { - let fd, r; - const {url, timeout, responseType, withCredentials, type, onprogress, form, headers, id} = e.detail; - window.FCX.requests[id] = (r = new XMLHttpRequest()); - r.open(type, url, true); - const object = headers || {}; - for (var key in object) { - var value = object[key]; - r.setRequestHeader(key, value); - } - r.responseType = responseType === 'document' ? 'text' : responseType; - r.timeout = timeout; - r.withCredentials = withCredentials; - if (onprogress) { - r.upload.onprogress = function(e) { - const {loaded, total} = e; - const detail = {loaded, total, id}; - return document.dispatchEvent(new CustomEvent('4chanXAjaxProgress', {bubbles: true, detail})); + if (platform === 'userscript') { + return r; + } else { + // # XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638 + let requestID = 0; + const requests = dict(); + + $.ajaxPageInit = function() { + $.global(function() { + window.FCX.requests = Object.create(null); + + document.addEventListener('4chanXAjax', function(e) { + let fd, r; + const {url, timeout, responseType, withCredentials, type, onprogress, form, headers, id} = e.detail; + window.FCX.requests[id] = (r = new XMLHttpRequest()); + r.open(type, url, true); + const object = headers || {}; + for (var key in object) { + var value = object[key]; + r.setRequestHeader(key, value); + } + r.responseType = responseType === 'document' ? 'text' : responseType; + r.timeout = timeout; + r.withCredentials = withCredentials; + if (onprogress) { + r.upload.onprogress = function(e) { + const {loaded, total} = e; + const detail = {loaded, total, id}; + return document.dispatchEvent(new CustomEvent('4chanXAjaxProgress', {bubbles: true, detail})); + }; + } + r.onloadend = function() { + delete window.FCX.requests[id]; + const {status, statusText, response} = this; + const responseHeaderString = this.getAllResponseHeaders(); + const detail = {status, statusText, response, responseHeaderString, id}; + return document.dispatchEvent(new CustomEvent('4chanXAjaxLoadend', {bubbles: true, detail})); }; - } - r.onloadend = function() { - delete window.FCX.requests[id]; - const {status, statusText, response} = this; - const responseHeaderString = this.getAllResponseHeaders(); - const detail = {status, statusText, response, responseHeaderString, id}; - return document.dispatchEvent(new CustomEvent('4chanXAjaxLoadend', {bubbles: true, detail})); - }; - // connection error or content blocker - r.onerror = function() { - if (!r.status) { return console.warn(`4chan X failed to load: ${url}`); } - }; - if (form) { - fd = new FormData(); - for (var entry of form) { - fd.append(entry[0], entry[1]); + // connection error or content blocker + r.onerror = function() { + if (!r.status) { return console.warn(`4chan X failed to load: ${url}`); } + }; + if (form) { + fd = new FormData(); + for (var entry of form) { + fd.append(entry[0], entry[1]); + } + } else { + fd = null; } - } else { - fd = null; - } - return r.send(fd); + return r.send(fd); } - , false); + , false); return document.addEventListener('4chanXAjaxAbort', function(e) { let r; @@ -218,24 +197,24 @@ $.ajax = (function() { }; return $.ajaxPage = function(url, options={}) { - let req; - let {onloadend, timeout, responseType, withCredentials, type, onprogress, form, headers} = options; - const id = requestID++; - requests[id] = (req = new CrossOrigin.Request()); - $.extend(req, {responseType, onloadend}); - req.upload = {onprogress}; - req.abort = () => $.event('4chanXAjaxAbort', {id}); - if (form) { form = Array.from(form.entries()); } - $.event('4chanXAjax', {url, timeout, responseType, withCredentials, type, onprogress: !!onprogress, form, headers, id}); - return req; - }; + let req; + let {onloadend, timeout, responseType, withCredentials, type, onprogress, form, headers} = options; + const id = requestID++; + requests[id] = (req = new CrossOrigin.Request()); + $.extend(req, {responseType, onloadend}); + req.upload = {onprogress}; + req.abort = () => $.event('4chanXAjaxAbort', {id}); + if (form) { form = Array.from(form.entries()); } + $.event('4chanXAjax', {url, timeout, responseType, withCredentials, type, onprogress: !!onprogress, form, headers, id}); + return req; + }; + } })(); -// <% } %> // Status Code 304: Not modified // With the `If-Modified-Since` header we only receive the HTTP headers and no body for 304 responses. // This saves a lot of bandwidth and CPU time for both the users and the servers. -$.lastModified = $.dict(); +$.lastModified = dict(); $.whenModified = function(url, bucket, cb, options={}) { let t; const {timeout, ajax} = options; @@ -245,13 +224,13 @@ $.whenModified = function(url, bucket, cb, options={}) { if (url.split('/')[2] === 'a.4cdn.org') { params.push(`t=${Date.now()}`); } const url0 = url; if (params.length) { url += '?' + params.join('&'); } - const headers = $.dict(); + const headers = dict(); if ((t = $.lastModified[bucket]?.[url0]) != null) { headers['If-Modified-Since'] = t; } const r = (ajax || $.ajax)(url, { onloadend() { - ($.lastModified[bucket] || ($.lastModified[bucket] = $.dict()))[url0] = this.getResponseHeader('Last-Modified'); + ($.lastModified[bucket] || ($.lastModified[bucket] = dict()))[url0] = this.getResponseHeader('Last-Modified'); return cb.call(this); }, timeout, @@ -261,7 +240,7 @@ $.whenModified = function(url, bucket, cb, options={}) { }; (function() { - const reqs = $.dict(); + const reqs = dict(); $.cache = function(url, cb, options={}) { let req; const {ajax} = options; @@ -438,55 +417,55 @@ $.one = function(el, events, handler) { }; $.event = function(event, detail, root=d) { - // <% if (type === 'userscript') { %> - if ((detail != null) && (typeof cloneInto === 'function')) { - detail = cloneInto(detail, d.defaultView); + if (!globalThis.chrome?.extension) { + if ((detail != null) && (typeof cloneInto === 'function')) { + detail = cloneInto(detail, d.defaultView); + } } - // <% } %> return root.dispatchEvent(new CustomEvent(event, {bubbles: true, cancelable: true, detail})); }; -// <% if (type === 'userscript') { %> -// XXX Make $.event work in Pale Moon with GM 3.x (no cloneInto function). -(function() { - if (!/PaleMoon\//.test(navigator.userAgent) || (+GM_info?.version?.split('.')[0] < 2) || (typeof cloneInto !== 'undefined')) { return; } +if (platform === 'userscript') { + // XXX Make $.event work in Pale Moon with GM 3.x (no cloneInto function). + (function() { + if (!/PaleMoon\//.test(navigator.userAgent) || (+GM_info?.version?.split('.')[0] < 2) || (typeof cloneInto !== 'undefined')) { return; } - try { - return new CustomEvent('x', {detail: {}}); - } catch (err) { - const unsafeConstructors = { - Object: unsafeWindow.Object, - Array: unsafeWindow.Array - }; - var clone = function(obj) { - let constructor; - if ((obj != null) && (typeof obj === 'object') && (constructor = unsafeConstructors[obj.constructor.name])) { - const obj2 = new constructor(); - for (var key in obj) { var val = obj[key]; obj2[key] = clone(val); } - return obj2; - } else { - return obj; - } - }; - return $.event = (event, detail, root=d) => root.dispatchEvent(new CustomEvent(event, {bubbles: true, cancelable: true, detail: clone(detail)})); - } -})(); -// <% } %> + try { + return new CustomEvent('x', {detail: {}}); + } catch (err) { + const unsafeConstructors = { + Object: unsafeWindow.Object, + Array: unsafeWindow.Array + }; + var clone = function(obj) { + let constructor; + if ((obj != null) && (typeof obj === 'object') && (constructor = unsafeConstructors[obj.constructor.name])) { + const obj2 = new constructor(); + for (var key in obj) { var val = obj[key]; obj2[key] = clone(val); } + return obj2; + } else { + return obj; + } + }; + return $.event = (event, detail, root=d) => root.dispatchEvent(new CustomEvent(event, {bubbles: true, cancelable: true, detail: clone(detail)})); + } + })(); +} $.modifiedClick = e => e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || (e.button !== 0); -// <% if (type === 'userscript') { %> -$.open = - (GM?.openInTab != null) ? - GM.openInTab - : (typeof GM_openInTab !== 'undefined' && GM_openInTab !== null) ? - GM_openInTab - : +if (!globalThis.chrome?.extension) { + $.open = + (GM?.openInTab != null) ? + GM.openInTab + : (typeof GM_openInTab !== 'undefined' && GM_openInTab !== null) ? + GM_openInTab + : + url => window.open(url, '_blank'); +} else { + $.open = url => window.open(url, '_blank'); -// <% } else { %> -$.open = - url => window.open(url, '_blank'); -// <% } %> +} $.debounce = function(wait, fn) { let lastCall = 0; @@ -596,8 +575,6 @@ $.engine = (function() { if (/Gecko\/|Goanna/.test(navigator.userAgent)) { return 'gecko'; } // Goanna = Pale Moon 26+ })(); -$.platform = '<%= type %>'; - $.hasStorage = (function() { try { if (localStorage.getItem(g.NAMESPACE + 'hasStorage') === 'true') { return true; } @@ -609,7 +586,7 @@ $.hasStorage = (function() { })(); $.item = function(key, val) { - const item = $.dict(); + const item = dict(); item[key] = val; return item; }; @@ -622,7 +599,7 @@ $.oneItemSugar = fn => (function(key, val, cb) { } }); -$.syncing = $.dict(); +$.syncing = dict(); $.securityCheck = function(data) { if (location.protocol !== 'https:') { @@ -630,387 +607,388 @@ $.securityCheck = function(data) { } }; -// <% if (type === 'crx') { %> -// https://developer.chrome.com/extensions/storage.html -$.oldValue = { - local: $.dict(), - sync: $.dict() -}; - -chrome.storage.onChanged.addListener(function(changes, area) { - for (var key in changes) { - var oldValue = $.oldValue.local[key] ?? $.oldValue.sync[key]; - $.oldValue[area][key] = $.dict.clone(changes[key].newValue); - var newValue = $.oldValue.local[key] ?? $.oldValue.sync[key]; - var cb = $.syncing[key]; - if (cb && (JSON.stringify(newValue) !== JSON.stringify(oldValue))) { - cb(newValue, key); - } - } -}); -$.sync = (key, cb) => $.syncing[key] = cb; -$.forceSync = function() { }; +if (platform === 'crx') { + // https://developer.chrome.com/extensions/storage.html + $.oldValue = { + local: dict(), + sync: dict() + }; -$.crxWorking = function() { - try { - if (chrome.runtime.getManifest()) { - return true; + chrome.storage.onChanged.addListener(function(changes, area) { + for (var key in changes) { + var oldValue = $.oldValue.local[key] ?? $.oldValue.sync[key]; + $.oldValue[area][key] = dict.clone(changes[key].newValue); + var newValue = $.oldValue.local[key] ?? $.oldValue.sync[key]; + var cb = $.syncing[key]; + if (cb && (JSON.stringify(newValue) !== JSON.stringify(oldValue))) { + cb(newValue, key); + } } - } catch (error) {} - if (!$.crxWarningShown) { - const msg = $.el('div', - {innerHTML: '4chan X seems to have been updated. You will need to reload the page.'}); - $.on($('a', msg), 'click', () => location.reload()); - new Notice('warning', msg); - $.crxWarningShown = true; - } - return false; -}; + }); + $.sync = (key, cb) => $.syncing[key] = cb; + $.forceSync = function() { }; -$.get = $.oneItemSugar(function(data, cb) { - if (!$.crxWorking()) { return; } - const results = {}; - const get = function(area) { - let keys = Object.keys(data); - // XXX slow performance in Firefox - if (($.engine === 'gecko') && (area === 'sync') && (keys.length > 3)) { - keys = null; - } - return chrome.storage[area].get(keys, function(result) { - let key; - result = $.dict.clone(result); - if (chrome.runtime.lastError) { - c.error(chrome.runtime.lastError.message); - } - if (keys === null) { - const result2 = $.dict(); - for (key in result) { var val = result[key]; if ($.hasOwn(data, key)) { result2[key] = val; } } - result = result2; - } - for (key in data) { - $.oldValue[area][key] = result[key]; - } - results[area] = result; - if (results.local && results.sync) { - $.extend(data, results.sync); - $.extend(data, results.local); - return cb(data); + $.crxWorking = function() { + try { + if (chrome.runtime.getManifest()) { + return true; } - }); + } catch (error) {} + if (!$.crxWarningShown) { + const msg = $.el('div', + {innerHTML: '4chan X seems to have been updated. You will need to reload the page.'}); + $.on($('a', msg), 'click', () => location.reload()); + new Notice('warning', msg); + $.crxWarningShown = true; + } + return false; }; - get('local'); - return get('sync'); -}); -(function() { - const items = { - local: $.dict(), - sync: $.dict() - }; + $.get = $.oneItemSugar(function(data, cb) { + if (!$.crxWorking()) { return; } + const results = {}; + const get = function(area) { + let keys = Object.keys(data); + // XXX slow performance in Firefox + if (($.engine === 'gecko') && (area === 'sync') && (keys.length > 3)) { + keys = null; + } + return chrome.storage[area].get(keys, function(result) { + let key; + result = dict.clone(result); + if (chrome.runtime.lastError) { + c.error(chrome.runtime.lastError.message); + } + if (keys === null) { + const result2 = dict(); + for (key in result) { var val = result[key]; if ($.hasOwn(data, key)) { result2[key] = val; } } + result = result2; + } + for (key in data) { + $.oldValue[area][key] = result[key]; + } + results[area] = result; + if (results.local && results.sync) { + $.extend(data, results.sync); + $.extend(data, results.local); + return cb(data); + } + }); + }; + get('local'); + return get('sync'); + }); - const exceedsQuota = (key, value) => // bytes in UTF-8 - unescape(encodeURIComponent(JSON.stringify(key))).length + unescape(encodeURIComponent(JSON.stringify(value))).length > chrome.storage.sync.QUOTA_BYTES_PER_ITEM; + (function() { + const items = { + local: dict(), + sync: dict() + }; - $.delete = function(keys) { - if (!$.crxWorking()) { return; } - if (typeof keys === 'string') { - keys = [keys]; - } - for (var key of keys) { - delete items.local[key]; - delete items.sync[key]; - } - chrome.storage.local.remove(keys); - return chrome.storage.sync.remove(keys); - }; + const exceedsQuota = (key, value) => // bytes in UTF-8 + unescape(encodeURIComponent(JSON.stringify(key))).length + unescape(encodeURIComponent(JSON.stringify(value))).length > chrome.storage.sync.QUOTA_BYTES_PER_ITEM; - const timeout = {}; - var setArea = function(area, cb) { - const data = $.dict(); - $.extend(data, items[area]); - if (!Object.keys(data).length || (timeout[area] > Date.now())) { return; } - return chrome.storage[area].set(data, function() { - let err; - let key; - if (err = chrome.runtime.lastError) { - c.error(err.message); - setTimeout(setArea, $.MINUTE, area); - timeout[area] = Date.now() + $.MINUTE; - return cb?.(err); + $.delete = function(keys) { + if (!$.crxWorking()) { return; } + if (typeof keys === 'string') { + keys = [keys]; + } + for (var key of keys) { + delete items.local[key]; + delete items.sync[key]; } + chrome.storage.local.remove(keys); + return chrome.storage.sync.remove(keys); + }; - delete timeout[area]; - for (key in data) { if (items[area][key] === data[key]) { delete items[area][key]; } } - if (area === 'local') { - for (key in data) { var val = data[key]; if (!exceedsQuota(key, val)) { items.sync[key] = val; } } - setSync(); - } else { - chrome.storage.local.remove(((() => { - const result = []; - for (key in data) { - if (!(key in items.local)) { - result.push(key); + const timeout = {}; + var setArea = function(area, cb) { + const data = $.dict(); + $.extend(data, items[area]); + if (!Object.keys(data).length || (timeout[area] > Date.now())) { return; } + return chrome.storage[area].set(data, function() { + let err; + let key; + if (err = chrome.runtime.lastError) { + c.error(err.message); + setTimeout(setArea, MINUTE, area); + timeout[area] = Date.now() + MINUTE; + return cb?.(err); + } + + delete timeout[area]; + for (key in data) { if (items[area][key] === data[key]) { delete items[area][key]; } } + if (area === 'local') { + for (key in data) { var val = data[key]; if (!exceedsQuota(key, val)) { items.sync[key] = val; } } + setSync(); + } else { + chrome.storage.local.remove(((() => { + const result = []; + for (key in data) { + if (!(key in items.local)) { + result.push(key); + } } - } - return result; - })())); - } - return cb?.(); - }); - }; + return result; + })())); + } + return cb?.(); + }); + }; - var setSync = $.debounce($.SECOND, () => setArea('sync')); + var setSync = debounce(SECOND, () => setArea('sync')); - $.set = $.oneItemSugar(function(data, cb) { - if (!$.crxWorking()) { return; } - $.securityCheck(data); - $.extend(items.local, data); - return setArea('local', cb); - }); + $.set = $.oneItemSugar(function(data, cb) { + if (!$.crxWorking()) { return; } + $.securityCheck(data); + $.extend(items.local, data); + return setArea('local', cb); + }); - return $.clear = function(cb) { - if (!$.crxWorking()) { return; } - items.local = $.dict(); - items.sync = $.dict(); - let count = 2; - let err = null; - const done = function() { - if (chrome.runtime.lastError) { - c.error(chrome.runtime.lastError.message); - } - if (err == null) { err = chrome.runtime.lastError; } - if (!--count) { return cb?.(err); } + return $.clear = function(cb) { + if (!$.crxWorking()) { return; } + items.local = dict(); + items.sync = dict(); + let count = 2; + let err = null; + const done = function() { + if (chrome.runtime.lastError) { + c.error(chrome.runtime.lastError.message); + } + if (err == null) { err = chrome.runtime.lastError; } + if (!--count) { return cb?.(err); } + }; + chrome.storage.local.clear(done); + return chrome.storage.sync.clear(done); }; - chrome.storage.local.clear(done); - return chrome.storage.sync.clear(done); - }; -})(); -// <% } else { %> + })(); +} else { -// http://wiki.greasespot.net/Main_Page -// https://tampermonkey.net/documentation.php + // http://wiki.greasespot.net/Main_Page + // https://tampermonkey.net/documentation.php -if ((GM?.deleteValue != null) && window.BroadcastChannel && (typeof GM_addValueChangeListener === 'undefined' || GM_addValueChangeListener === null)) { + if ((GM?.deleteValue != null) && window.BroadcastChannel && (typeof GM_addValueChangeListener === 'undefined' || GM_addValueChangeListener === null)) { - $.syncChannel = new BroadcastChannel(g.NAMESPACE + 'sync'); + $.syncChannel = new BroadcastChannel(g.NAMESPACE + 'sync'); - $.on($.syncChannel, 'message', e => (() => { - const result = []; - for (var key in e.data) { - var cb; - var val = e.data[key]; - if (cb = $.syncing[key]) { - result.push(cb($.dict.json(JSON.stringify(val)), key)); + $.on($.syncChannel, 'message', e => (() => { + const result = []; + for (var key in e.data) { + var cb; + var val = e.data[key]; + if (cb = $.syncing[key]) { + result.push(cb(dict.json(JSON.stringify(val)), key)); + } } - } - return result; - })()); + return result; + })()); - $.sync = (key, cb) => $.syncing[key] = cb; + $.sync = (key, cb) => $.syncing[key] = cb; - $.forceSync = function() {}; + $.forceSync = function() {}; - $.delete = function(keys, cb) { - let key; - if (!(keys instanceof Array)) { - keys = [keys]; - } - return Promise.all((() => { - const result = []; - for (key of keys) { result.push(GM.deleteValue(g.NAMESPACE + key)); + $.delete = function(keys, cb) { + let key; + if (!(keys instanceof Array)) { + keys = [keys]; } - return result; - })()).then(function() { - const items = $.dict(); - for (key of keys) { items[key] = undefined; } - $.syncChannel.postMessage(items); - return cb?.(); - }); - }; + return Promise.all((() => { + const result = []; + for (key of keys) { result.push(GM.deleteValue(g.NAMESPACE + key)); + } + return result; + })()).then(function() { + const items = $.dict(); + for (key of keys) { items[key] = undefined; } + $.syncChannel.postMessage(items); + return cb?.(); + }); + }; - $.get = $.oneItemSugar(function(items, cb) { - const keys = Object.keys(items); - return Promise.all(keys.map((key) => GM.getValue(g.NAMESPACE + key))).then(function(values) { - for (let i = 0; i < values.length; i++) { - var val = values[i]; - if (val) { - items[keys[i]] = $.dict.json(val); + $.get = $.oneItemSugar(function(items, cb) { + const keys = Object.keys(items); + return Promise.all(keys.map((key) => GM.getValue(g.NAMESPACE + key))).then(function(values) { + for (let i = 0; i < values.length; i++) { + var val = values[i]; + if (val) { + items[keys[i]] = dict.json(val); + } } - } - return cb(items); + return cb(items); + }); }); - }); - $.set = $.oneItemSugar(function(items, cb) { - $.securityCheck(items); - return Promise.all((() => { - const result = []; - for (var key in items) { - var val = items[key]; - result.push(GM.setValue(g.NAMESPACE + key, JSON.stringify(val))); - } - return result; - })()).then(function() { - $.syncChannel.postMessage(items); - return cb?.(); + $.set = $.oneItemSugar(function(items, cb) { + $.securityCheck(items); + return Promise.all((() => { + const result = []; + for (var key in items) { + var val = items[key]; + result.push(GM.setValue(g.NAMESPACE + key, JSON.stringify(val))); + } + return result; + })()).then(function() { + $.syncChannel.postMessage(items); + return cb?.(); + }); }); - }); - - $.clear = cb => GM.listValues().then(keys => $.delete(keys.map(key => key.replace(g.NAMESPACE, '')), cb)).catch( () => $.delete(Object.keys(Conf).concat(['previousversion', 'QR Size', 'QR.persona']), cb)); -} else { - if (typeof GM_deleteValue === 'undefined' || GM_deleteValue === null) { - $.perProtocolSettings = true; - } - - if (typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) { - $.getValue = GM_getValue; - $.listValues = () => GM_listValues(); // error when called if missing - } else if ($.hasStorage) { - $.getValue = key => localStorage.getItem(key); - $.listValues = () => (() => { - const result = []; - for (var key in localStorage) { - if (key.slice(0, g.NAMESPACE.length) === g.NAMESPACE) { - result.push(key); - } - } - return result; - })(); + $.clear = cb => GM.listValues().then(keys => $.delete(keys.map(key => key.replace(g.NAMESPACE, '')), cb)).catch( () => $.delete(Object.keys(Conf).concat(['previousversion', 'QR Size', 'QR.persona']), cb)); } else { - $.getValue = function() {}; - $.listValues = () => []; - } - if (typeof GM_addValueChangeListener !== 'undefined' && GM_addValueChangeListener !== null) { - $.setValue = GM_setValue; - $.deleteValue = GM_deleteValue; - } else if (typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) { - $.oldValue = $.dict(); - $.setValue = function(key, val) { - GM_setValue(key, val); - if (key in $.syncing) { - $.oldValue[key] = val; - if ($.hasStorage) { return localStorage.setItem(key, val); } // for `storage` events - } - }; - $.deleteValue = function(key) { - GM_deleteValue(key); - if (key in $.syncing) { - delete $.oldValue[key]; - if ($.hasStorage) { return localStorage.removeItem(key); } // for `storage` events - } - }; - if (!$.hasStorage) { $.cantSync = true; } - } else if ($.hasStorage) { - $.oldValue = $.dict(); - $.setValue = function(key, val) { - if (key in $.syncing) { $.oldValue[key] = val; } - return localStorage.setItem(key, val); - }; - $.deleteValue = function(key) { - if (key in $.syncing) { delete $.oldValue[key]; } - return localStorage.removeItem(key); - }; - } else { - $.setValue = function() {}; - $.deleteValue = function() {}; - $.cantSync = ($.cantSet = true); - } + if (typeof GM_deleteValue === 'undefined' || GM_deleteValue === null) { + $.perProtocolSettings = true; + } - if (typeof GM_addValueChangeListener !== 'undefined' && GM_addValueChangeListener !== null) { - $.sync = (key, cb) => $.syncing[key] = GM_addValueChangeListener(g.NAMESPACE + key, function(key2, oldValue, newValue, remote) { - if (remote) { - if (newValue !== undefined) { newValue = $.dict.json(newValue); } - return cb(newValue, key); - } - }); - $.forceSync = function() {}; - } else if ((typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) || $.hasStorage) { - $.sync = function(key, cb) { - key = g.NAMESPACE + key; - $.syncing[key] = cb; - return $.oldValue[key] = $.getValue(key); - }; + if (typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) { + $.getValue = GM_getValue; + $.listValues = () => GM_listValues(); // error when called if missing + } else if ($.hasStorage) { + $.getValue = key => localStorage.getItem(key); + $.listValues = () => (() => { + const result = []; + for (var key in localStorage) { + if (key.slice(0, g.NAMESPACE.length) === g.NAMESPACE) { + result.push(key); + } + } + return result; + })(); + } else { + $.getValue = function() {}; + $.listValues = () => []; + } - (function() { - const onChange = function({key, newValue}) { - let cb; - if (!(cb = $.syncing[key])) { return; } - if (newValue != null) { - if (newValue === $.oldValue[key]) { return; } - $.oldValue[key] = newValue; - return cb($.dict.json(newValue), key.slice(g.NAMESPACE.length)); - } else { - if ($.oldValue[key] == null) { return; } + if (typeof GM_addValueChangeListener !== 'undefined' && GM_addValueChangeListener !== null) { + $.setValue = GM_setValue; + $.deleteValue = GM_deleteValue; + } else if (typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) { + $.oldValue = dict(); + $.setValue = function(key, val) { + GM_setValue(key, val); + if (key in $.syncing) { + $.oldValue[key] = val; + if ($.hasStorage) { return localStorage.setItem(key, val); } // for `storage` events + } + }; + $.deleteValue = function(key) { + GM_deleteValue(key); + if (key in $.syncing) { delete $.oldValue[key]; - return cb(undefined, key.slice(g.NAMESPACE.length)); + if ($.hasStorage) { return localStorage.removeItem(key); } // for `storage` events } }; - $.on(window, 'storage', onChange); + if (!$.hasStorage) { $.cantSync = true; } + } else if ($.hasStorage) { + $.oldValue = dict(); + $.setValue = function(key, val) { + if (key in $.syncing) { $.oldValue[key] = val; } + return localStorage.setItem(key, val); + }; + $.deleteValue = function(key) { + if (key in $.syncing) { delete $.oldValue[key]; } + return localStorage.removeItem(key); + }; + } else { + $.setValue = function() {}; + $.deleteValue = function() {}; + $.cantSync = ($.cantSet = true); + } - return $.forceSync = function(key) { - // Storage events don't work across origins - // e.g. http://boards.4chan.org and https://boards.4chan.org - // so force a check for changes to avoid lost data. + if (typeof GM_addValueChangeListener !== 'undefined' && GM_addValueChangeListener !== null) { + $.sync = (key, cb) => $.syncing[key] = GM_addValueChangeListener(g.NAMESPACE + key, function(key2, oldValue, newValue, remote) { + if (remote) { + if (newValue !== undefined) { newValue = dict.json(newValue); } + return cb(newValue, key); + } + }); + $.forceSync = function() {}; + } else if ((typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) || $.hasStorage) { + $.sync = function(key, cb) { key = g.NAMESPACE + key; - return onChange({key, newValue: $.getValue(key)}); + $.syncing[key] = cb; + return $.oldValue[key] = $.getValue(key); }; - })(); - } else { - $.sync = function() {}; - $.forceSync = function() {}; - } - $.delete = function(keys) { - if (!(keys instanceof Array)) { - keys = [keys]; - } - for (var key of keys) { - $.deleteValue(g.NAMESPACE + key); + (function() { + const onChange = function({key, newValue}) { + let cb; + if (!(cb = $.syncing[key])) { return; } + if (newValue != null) { + if (newValue === $.oldValue[key]) { return; } + $.oldValue[key] = newValue; + return cb(dict.json(newValue), key.slice(g.NAMESPACE.length)); + } else { + if ($.oldValue[key] == null) { return; } + delete $.oldValue[key]; + return cb(undefined, key.slice(g.NAMESPACE.length)); + } + }; + $.on(window, 'storage', onChange); + + return $.forceSync = function(key) { + // Storage events don't work across origins + // e.g. http://boards.4chan.org and https://boards.4chan.org + // so force a check for changes to avoid lost data. + key = g.NAMESPACE + key; + return onChange({key, newValue: $.getValue(key)}); + }; + })(); + } else { + $.sync = function() {}; + $.forceSync = function() {}; } - }; - $.get = $.oneItemSugar((items, cb) => $.queueTask($.getSync, items, cb)); - - $.getSync = function(items, cb) { - for (var key in items) { - var val2; - if (val2 = $.getValue(g.NAMESPACE + key)) { - try { - items[key] = $.dict.json(val2); - } catch (err) { - // XXX https://github.com/ccd0/4chan-x/issues/2218 - if (!/^(?:undefined)*$/.test(val2)) { - throw err; - } - } + $.delete = function(keys) { + if (!(keys instanceof Array)) { + keys = [keys]; } - } - return cb(items); - }; + for (var key of keys) { + $.deleteValue(g.NAMESPACE + key); + } + }; + + $.get = $.oneItemSugar((items, cb) => $.queueTask($.getSync, items, cb)); - $.set = $.oneItemSugar(function(items, cb) { - $.securityCheck(items); - return $.queueTask(function() { + $.getSync = function(items, cb) { for (var key in items) { - var value = items[key]; - $.setValue(g.NAMESPACE + key, JSON.stringify(value)); + var val2; + if (val2 = $.getValue(g.NAMESPACE + key)) { + try { + items[key] = dict.json(val2); + } catch (err) { + // XXX https://github.com/ccd0/4chan-x/issues/2218 + if (!/^(?:undefined)*$/.test(val2)) { + throw err; + } + } + } } - return cb?.(); + return cb(items); + }; + + $.set = $.oneItemSugar(function(items, cb) { + $.securityCheck(items); + return $.queueTask(function() { + for (var key in items) { + var value = items[key]; + $.setValue(g.NAMESPACE + key, JSON.stringify(value)); + } + return cb?.(); + }); }); - }); - $.clear = function(cb) { - // XXX https://github.com/greasemonkey/greasemonkey/issues/2033 - // Also support case where GM_listValues is not defined. - $.delete(Object.keys(Conf)); - $.delete(['previousversion', 'QR Size', 'QR.persona']); - try { - $.delete($.listValues().map(key => key.replace(g.NAMESPACE, ''))); - } catch (error) {} - return cb?.(); - }; + $.clear = function(cb) { + // XXX https://github.com/greasemonkey/greasemonkey/issues/2033 + // Also support case where GM_listValues is not defined. + $.delete(Object.keys(Conf)); + $.delete(['previousversion', 'QR Size', 'QR.persona']); + try { + $.delete($.listValues().map(key => key.replace(g.NAMESPACE, ''))); + } catch (error) {} + return cb?.(); + }; + } } -// <% } %> +export default $; diff --git a/src/platform/CrossOrigin.js b/src/platform/CrossOrigin.js index d025550698..f790c22ad1 100644 --- a/src/platform/CrossOrigin.js +++ b/src/platform/CrossOrigin.js @@ -1,3 +1,7 @@ +import QR from "../Posting/QR"; +import $ from "./$"; +import { dict, platform } from "./helpers"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -6,80 +10,78 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -// <% if (type === 'crx') { %> -let Request; -const eventPageRequest = (function() { - const callbacks = []; - chrome.runtime.onMessage.addListener(function(response) { - callbacks[response.id](response.data); - return delete callbacks[response.id];}); - return (params, cb) => chrome.runtime.sendMessage(params, id => callbacks[id] = cb); -})(); - -// <% } %> +let eventPageRequest; +if (platform === 'crx') { + eventPageRequest = (function () { + const callbacks = []; + chrome.runtime.onMessage.addListener(function(response) { + callbacks[response.id](response.data); + return delete callbacks[response.id];}); + return (params, cb) => chrome.runtime.sendMessage(params, id => callbacks[id] = cb); + })(); +} var CrossOrigin = { - binary(url, cb, headers=$.dict()) { + binary(url, cb, headers = dict()) { // XXX https://forums.lanik.us/viewtopic.php?f=64&t=24173&p=78310 url = url.replace(/^((?:https?:)?\/\/(?:\w+\.)?(?:4chan|4channel|4cdn)\.org)\/adv\//, '$1//adv/'); - // <% if (type === 'crx') { %> + if (platform === 'crx') { eventPageRequest({type: 'ajax', url, headers, responseType: 'arraybuffer'}, function({response, responseHeaderString}) { if (response) { response = new Uint8Array(response); } return cb(response, responseHeaderString); }); - // <% } %> - // <% if (type === 'userscript') { %> - const fallback = function() { - return $.ajax(url, { + } else { + const fallback = function() { + return $.ajax(url, { + headers, + responseType: 'arraybuffer', + onloadend() { + if (this.status && this.response) { + return cb(new Uint8Array(this.response), this.getAllResponseHeaders()); + } else { + return cb(null); + } + } + }); + }; + if ((typeof window.GM_xmlhttpRequest === 'undefined' || window.GM_xmlhttpRequest === null)) { + fallback(); + return; + } + const gmOptions = { + method: "GET", + url, headers, responseType: 'arraybuffer', - onloadend() { - if (this.status && this.response) { - return cb(new Uint8Array(this.response), this.getAllResponseHeaders()); + overrideMimeType: 'text/plain; charset=x-user-defined', + onload(xhr) { + let data; + if (xhr.response instanceof ArrayBuffer) { + data = new Uint8Array(xhr.response); } else { - return cb(null); - } - } - }); - }; - if ((GM?.xmlHttpRequest == null) && (typeof GM_xmlhttpRequest === 'undefined' || GM_xmlhttpRequest === null)) { - fallback(); - return; - } - const gmOptions = { - method: "GET", - url, - headers, - responseType: 'arraybuffer', - overrideMimeType: 'text/plain; charset=x-user-defined', - onload(xhr) { - let data; - if (xhr.response instanceof ArrayBuffer) { - data = new Uint8Array(xhr.response); - } else { - const r = xhr.responseText; - data = new Uint8Array(r.length); - let i = 0; - while (i < r.length) { - data[i] = r.charCodeAt(i); - i++; + const r = xhr.responseText; + data = new Uint8Array(r.length); + let i = 0; + while (i < r.length) { + data[i] = r.charCodeAt(i); + i++; + } } + return cb(data, xhr.responseHeaders); + }, + onerror() { + return cb(null); + }, + onabort() { + return cb(null); } - return cb(data, xhr.responseHeaders); - }, - onerror() { - return cb(null); - }, - onabort() { - return cb(null); + }; + try { + return (GM?.xmlHttpRequest || GM_xmlhttpRequest)(gmOptions); + } catch (error) { + return fallback(); } - }; - try { - return (GM?.xmlHttpRequest || GM_xmlhttpRequest)(gmOptions); - } catch (error) { - return fallback(); } }, - // <% } %> file(url, cb) { return CrossOrigin.binary(url, function(data, headers) { @@ -104,8 +106,8 @@ var CrossOrigin = { }); }, - Request: (Request = (function() { - Request = class Request { + Request: (function() { + const Request = class Request { static initClass() { this.prototype.status = 0; this.prototype.statusText = ''; @@ -131,7 +133,7 @@ var CrossOrigin = { }; Request.initClass(); return Request; - })()), + })(), // Attempts to fetch `url` using cross-origin privileges, if available. // Interface is a subset of that of $.ajax. @@ -151,65 +153,61 @@ var CrossOrigin = { let {onloadend, timeout, responseType, headers} = options; if (responseType == null) { responseType = 'json'; } - // <% if (type === 'userscript') { %> - if ((GM?.xmlHttpRequest == null) && (typeof GM_xmlhttpRequest === 'undefined' || GM_xmlhttpRequest === null)) { + if ((window.GM?.xmlHttpRequest == null) && (typeof window.GM_xmlhttpRequest === 'undefined' || window.GM_xmlhttpRequest === null)) { return $.ajax(url, options); } - // <% } %> const req = new CrossOrigin.Request(); req.onloadend = onloadend; - // <% if (type === 'userscript') { %> - const gmOptions = { - method: 'GET', - url, - headers, - timeout, - onload(xhr) { - try { - const response = (() => { switch (responseType) { - case 'json': - if (xhr.responseText) { return JSON.parse(xhr.responseText); } else { return null; } - default: - return xhr.responseText; - } })(); - $.extend(req, { - response, - status: xhr.status, - statusText: xhr.statusText, - responseHeaderString: xhr.responseHeaders - }); - } catch (error) {} - return req.onloadend(); - }, - onerror() { return req.onloadend(); }, - onabort() { return req.onloadend(); }, - ontimeout() { return req.onloadend(); } - }; - try { - gmReq = (GM?.xmlHttpRequest || GM_xmlhttpRequest)(gmOptions); - } catch (error) { - return $.ajax(url, options); - } - - if (gmReq && (typeof gmReq.abort === 'function')) { - req.abort = function() { - try { - return gmReq.abort(); - } catch (error1) {} + if (platform === 'userscript') { + const gmOptions = { + method: 'GET', + url, + headers, + timeout, + onload(xhr) { + try { + const response = (() => { switch (responseType) { + case 'json': + if (xhr.responseText) { return JSON.parse(xhr.responseText); } else { return null; } + default: + return xhr.responseText; + } })(); + $.extend(req, { + response, + status: xhr.status, + statusText: xhr.statusText, + responseHeaderString: xhr.responseHeaders + }); + } catch (error) {} + return req.onloadend(); + }, + onerror() { return req.onloadend(); }, + onabort() { return req.onloadend(); }, + ontimeout() { return req.onloadend(); } }; - } - // <% } %> + try { + gmReq = (GM?.xmlHttpRequest || GM_xmlhttpRequest)(gmOptions); + } catch (error) { + return $.ajax(url, options); + } - // <% if (type === 'crx') { %> - eventPageRequest({type: 'ajax', url, responseType, headers, timeout}, function(result) { - if (result.status) { - $.extend(req, result); + if (gmReq && (typeof gmReq.abort === 'function')) { + req.abort = function() { + try { + return gmReq.abort(); + } catch (error1) {} + }; } - return req.onloadend(); - }); - // <% } %> + } else { + eventPageRequest({type: 'ajax', url, responseType, headers, timeout}, function(result) { + if (result.status) { + $.extend(req, result); + } + return req.onloadend(); + }); + } return req; }, @@ -219,20 +217,17 @@ var CrossOrigin = { {ajax: CrossOrigin.ajax}); }, - // <% if (type === 'crx') { %> permission(cb, cbFail, origins) { - return eventPageRequest({type: 'permission', origins}, function(result) { - if (result) { - return cb(); - } else { - return cbFail(); - } - }); - }, - // <% } %> - // <% if (type === 'userscript') { %> - permission(cb) { + if (platform === 'crx') { + return eventPageRequest({type: 'permission', origins}, function(result) { + if (result) { + return cb(); + } else { + return cbFail(); + } + }); + } return cb(); - } + }, }; - // <% } %> +export default CrossOrigin; diff --git a/src/platform/helpers.ts b/src/platform/helpers.ts new file mode 100644 index 0000000000..63db5aa6b2 --- /dev/null +++ b/src/platform/helpers.ts @@ -0,0 +1,54 @@ +// This file was created because these functions on $ were sometimes not initialized yet because of circular +// dependencies, so try to keep this file without dependencies, so these functions don't have to wait for something else + +export const debounce = (wait: number, fn: Function) => { + let lastCall = 0; + let timeout = null; + let that = null; + let args = null; + const exec = function () { + lastCall = Date.now(); + return fn.apply(that, args); + }; + return function () { + args = arguments; + that = this; + if (lastCall < (Date.now() - wait)) { + return exec(); + } + // stop current reset + clearTimeout(timeout); + // after wait, let next invocation execute immediately + return timeout = setTimeout(exec, wait); + }; +}; + +export const dict = () => Object.create(null); + +dict.clone = function (obj) { + if ((typeof obj !== 'object') || (obj === null)) { + return obj; + } else if (obj instanceof Array) { + const arr = []; + for (let i = 0, end = obj.length; i < end; i++) { + arr.push(dict.clone(obj[i])); + } + return arr; + } else { + const map = Object.create(null); + for (var key in obj) { + var val = obj[key]; + map[key] = dict.clone(val); + } + return map; + } +}; + +dict.json = (str: string) => dict.clone(JSON.parse(str)); + +export const SECOND = 1000; +export const MINUTE = SECOND * 60; +export const HOUR = MINUTE * 60; +export const DAY = HOUR * 24; + +export const platform = window.GM_xmlhttpRequest ? 'userscript' : 'crx'; diff --git a/src/site/SW.js b/src/site/SW.js index 120a971ba3..f5f8adbd33 100644 --- a/src/site/SW.js +++ b/src/site/SW.js @@ -1 +1,5 @@ -SW = {}; +import SWTinyboard from "./SW.tinyboard"; +import SWYotsuba from "./SW.yotsuba"; + +const SW = { tinyboard: SWTinyboard, yotsuba: SWYotsuba }; +export default SW; diff --git a/src/site/SW.tinyboard.js b/src/site/SW.tinyboard.js index d7e2b978b0..89e3985408 100644 --- a/src/site/SW.tinyboard.js +++ b/src/site/SW.tinyboard.js @@ -1,9 +1,15 @@ +import { Conf, d } from "../globals/globals"; +import Main from "../main/Main"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import { dict } from "../platform/helpers"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ -SW.tinyboard = { +const SWTinyboard = { isOPContainerThread: true, mayLackJSON: true, threadModTimeIgnoresSage: true, @@ -36,7 +42,7 @@ SW.tinyboard = { for (var script of $$('script:not([src])', d.head)) { var m; if (m = script.textContent.match(/\bvar configRoot=(".*?")/)) { - var properties = $.dict(); + var properties = dict(); try { var root = JSON.parse(m[1]); if (root[0] === '/') { @@ -75,7 +81,7 @@ SW.tinyboard = { if (root) { return `${root}${boardID}/${isArchived ? 'archive/' : ''}res/${threadID}.json`; } else { return ''; } }, archivedThreadJSON(thread) { - return SW.tinyboard.urls.threadJSON(thread, true); + return SWTinyboard.urls.threadJSON(thread, true); }, threadsListJSON({siteID, boardID}) { const root = Conf['siteProperties'][siteID]?.root; @@ -93,7 +99,7 @@ SW.tinyboard = { return `${Conf['siteProperties'][siteID]?.root || `http://${siteID}/`}${boardID}/${filename}`; }, thumb(board, filename) { - return SW.tinyboard.urls.file(board, filename); + return SWTinyboard.urls.file(board, filename); } }, @@ -180,7 +186,7 @@ $\ Build: { parseJSON(data, board) { - const o = SW.yotsuba.Build.parseJSON(data, board); + const o = this.parseJSON(data, board); if (data.ext === 'deleted') { delete o.file; $.extend(o, { @@ -196,7 +202,7 @@ $\ if (extra_file.ext === 'deleted') { o.filesDeleted.push(i); } else { - file = SW.yotsuba.Build.parseJSONFile(data, board); + file = this.parseJSONFile(data, board); o.files.push(file); } } @@ -296,3 +302,4 @@ $\ return threadRoot.dataset.sticky = 'true'; } }; +export default SWTinyboard; diff --git a/src/site/SW.yotsuba.Build.js b/src/site/SW.yotsuba.Build.js deleted file mode 100644 index 655ca0a62b..0000000000 --- a/src/site/SW.yotsuba.Build.js +++ /dev/null @@ -1,346 +0,0 @@ -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -import PostInfoPage from './SW.yotsuba.Build/PostInfo.html'; -import FilePage from './SW.yotsuba.Build/File.html'; -import PostPage from './SW.yotsuba.Build/Post.html'; -import CatalogThreadPage from './SW.yotsuba.Build/CatalogThread.html'; -import CatalogReplyPage from './SW.yotsuba.Build/CatalogReply.html'; - -var Build = { - staticPath: '//s.4cdn.org/image/', - gifIcon: window.devicePixelRatio >= 2 ? '@2x.gif' : '.gif', - spoilerRange: $.dict(), - - shortFilename(filename) { - const ext = filename.match(/\.?[^\.]*$/)[0]; - if ((filename.length - ext.length) > 30) { - return `${filename.match(/(?:[\uD800-\uDBFF][\uDC00-\uDFFF]|[^]){0,25}/)[0]}(...)${ext}`; - } else { - return filename; - } - }, - - spoilerThumb(boardID) { - let spoilerRange; - if ((spoilerRange = Build.spoilerRange[boardID])) { - // Randomize the spoiler image. - return `${Build.staticPath}spoiler-${boardID}${Math.floor(1 + (spoilerRange * Math.random()))}.png`; - } else { - return `${Build.staticPath}spoiler.png`; - } - }, - - sameThread(boardID, threadID) { - return (g.VIEW === 'thread') && (g.BOARD.ID === boardID) && (g.THREADID === +threadID); - }, - - threadURL(boardID, threadID) { - if (boardID !== g.BOARD.ID) { - return `//${BoardConfig.domain(boardID)}/${boardID}/thread/${threadID}`; - } else if ((g.VIEW !== 'thread') || (+threadID !== g.THREADID)) { - return `/${boardID}/thread/${threadID}`; - } else { - return ''; - } - }, - - postURL(boardID, threadID, postID) { - return `${Build.threadURL(boardID, threadID)}#p${postID}`; - }, - - parseJSON(data, {siteID, boardID}) { - const o = { - // id - ID: data.no, - postID: data.no, - threadID: data.resto || data.no, - boardID, - siteID, - isReply: !!data.resto, - // thread status - isSticky: !!data.sticky, - isClosed: !!data.closed, - isArchived: !!data.archived, - // file status - fileDeleted: !!data.filedeleted, - filesDeleted: data.filedeleted ? [0] : [] - }; - o.info = { - subject: $.unescape(data.sub), - email: $.unescape(data.email), - name: $.unescape(data.name) || '', - tripcode: data.trip, - pass: (data.since4pass != null) ? `${data.since4pass}` : undefined, - uniqueID: data.id, - flagCode: data.country, - flagCodeTroll: data.board_flag, - flag: $.unescape((data.country_name || data.flag_name)), - dateUTC: data.time, - dateText: data.now, - commentHTML: {innerHTML: data.com || ''} - }; - if (data.capcode) { - o.info.capcode = data.capcode.replace(/_highlight$/, '').replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); - o.capcodeHighlight = /_highlight$/.test(data.capcode); - delete o.info.uniqueID; - } - o.files = []; - if (data.ext) { - o.file = SW.yotsuba.Build.parseJSONFile(data, {siteID, boardID}); - o.files.push(o.file); - } - // Temporary JSON properties for events such as April 1 / Halloween - o.extra = $.dict(); - for (var key in data) { - if (key[0] === 'x') { - o.extra[key] = data[key]; - } - } - return o; - }, - - parseJSONFile(data, {siteID, boardID}) { - const site = g.sites[siteID]; - const filename = (site.software === 'yotsuba') && (boardID === 'f') ? - `${encodeURIComponent(data.filename)}${data.ext}` - : - `${data.tim}${data.ext}`; - const o = { - name: ($.unescape(data.filename)) + data.ext, - url: site.urls.file({siteID, boardID}, filename), - height: data.h, - width: data.w, - MD5: data.md5, - size: $.bytesToString(data.fsize), - thumbURL: site.urls.thumb({siteID, boardID}, `${data.tim}s.jpg`), - theight: data.tn_h, - twidth: data.tn_w, - isSpoiler: !!data.spoiler, - tag: data.tag, - hasDownscale: !!data.m_img - }; - if ((data.h != null) && !/\.pdf$/.test(o.url)) { o.dimensions = `${o.width}x${o.height}`; } - return o; - }, - - parseComment(html) { - html = html - .replace(//gi, '\n') - .replace(/\n\n]*>/g, ''); - return $.unescape(html); - }, - - parseCommentDisplay(html) { - // Hide spoilers. - if (!Conf['Remove Spoilers'] && !Conf['Reveal Spoilers']) { - let html2; - while ((html2 = html.replace(/(?:(?!<\/?s>).)*<\/s>/g, '[spoiler]')) !== html) { - html = html2; - } - } - html = html - .replace(/^Rolled [^<]*<\/b>/i, '') // Rolls (/tg/, /qst/) - .replace(/ 1 ? 's' : ''}`; - if (+files) { text += ` and ${files} image repl${files > 1 ? 'ies' : 'y'}`; } - return text += ` ${status === '-' ? 'shown' : 'omitted'}.`; - }, - - summary(boardID, threadID, posts, files) { - return $.el('a', { - className: 'summary', - textContent: Build.summaryText('', posts, files), - href: `/${boardID}/thread/${threadID}` - } - ); - }, - - thread(thread, data, withReplies) { - let root; - if (root = thread.nodes.root) { - $.rmAll(root); - } else { - thread.nodes.root = (root = $.el('div', { - className: 'thread', - id: `t${data.no}` - } - )); - } - if (Build.hat) { $.add(root, Build.hat.cloneNode(false)); } - $.add(root, thread.OP.nodes.root); - if (data.omitted_posts || (!withReplies && data.replies)) { - const [posts, files] = Array.from(withReplies ? - // XXX data.omitted_images is not accurate. - [data.omitted_posts, data.images - data.last_replies.filter(data => !!data.ext).length] - : - [data.replies, data.images]); - const summary = Build.summary(thread.board.ID, data.no, posts, files); - $.add(root, summary); - } - return root; - }, - - catalogThread(thread, data, pageCount) { - let cssText, imgClass, src; - const {staticPath, gifIcon} = Build; - const {tn_w, tn_h} = data; - - if (data.spoiler && !Conf['Reveal Spoiler Thumbnails']) { - let spoilerRange; - src = `${staticPath}spoiler`; - if (spoilerRange = Build.spoilerRange[thread.board]) { - // Randomize the spoiler image. - src += (`-${thread.board}`) + Math.floor(1 + (spoilerRange * Math.random())); - } - src += '.png'; - imgClass = 'spoiler-file'; - cssText = "--tn-w: 100; --tn-h: 100;"; - } else if (data.filedeleted) { - src = `${staticPath}filedeleted-res${gifIcon}`; - imgClass = 'deleted-file'; - } else if (thread.OP.file) { - src = thread.OP.file.thumbURL; - const ratio = 250 / Math.max(tn_w, tn_h); - cssText = `--tn-w: ${tn_w * ratio}; --tn-h: ${tn_h * ratio};`; - } else { - src = `${staticPath}nofile.png`; - imgClass = 'no-file'; - } - - const postCount = data.replies + 1; - const fileCount = data.images + !!data.ext; - - const container = $.el('div', { innerHTML: CatalogThreadPage }); - $.before(thread.OP.nodes.info, [...Array.from(container.childNodes)]); - - for (var br of $$('br', thread.OP.nodes.comment)) { - if (br.previousSibling && (br.previousSibling.nodeName === 'BR')) { - $.addClass(br, 'extra-linebreak'); - } - } - - const root = $.el('div', { - className: 'thread catalog-thread', - id: `t${thread}` - } - ); - if (thread.OP.highlights) { $.addClass(root, ...Array.from(thread.OP.highlights)); } - if (!thread.OP.file) { $.addClass(root, 'noFile'); } - root.style.cssText = cssText || ''; - - return root; - }, - - catalogReply(thread, data) { - let excerpt = ''; - if (data.com) { - excerpt = Build.parseCommentDisplay(data.com).replace(/>>\d+/g, '').trim().replace(/\n+/g, ' // '); - } - if (data.ext) { - if (!excerpt) { excerpt = `${$.unescape(data.filename)}${data.ext}`; } - } - if (data.com) { - if (!excerpt) { excerpt = $.unescape(data.com.replace(//gi, ' // ')); } - } - if (!excerpt) { excerpt = '\xA0'; } - if (excerpt.length > 73) { excerpt = `${excerpt.slice(0, 70)}...`; } - - const link = Build.postURL(thread.board.ID, thread.ID, data.no); - return $.el('div', {className: 'catalog-reply'}, - { innerHTML: CatalogReplyPage }); - } -}; - -SW.yotsuba.Build = Build; diff --git a/src/site/SW.yotsuba.Build/CatalogReply.html b/src/site/SW.yotsuba.Build/CatalogReply.html deleted file mode 100644 index 6bd68c0139..0000000000 --- a/src/site/SW.yotsuba.Build/CatalogReply.html +++ /dev/null @@ -1,3 +0,0 @@ -: -${excerpt} -... diff --git a/src/site/SW.yotsuba.Build/CatalogThread.html b/src/site/SW.yotsuba.Build/CatalogThread.html deleted file mode 100644 index 70aacb3baa..0000000000 --- a/src/site/SW.yotsuba.Build/CatalogThread.html +++ /dev/null @@ -1,14 +0,0 @@ - - - -
- - ${postCount} / - ${fileCount} / - ${pageCount} - - - ?{thread.isSticky}{} - ?{thread.isClosed}{} - -
diff --git a/src/site/SW.yotsuba.Build/CatalogThreadHtml.tsx b/src/site/SW.yotsuba.Build/CatalogThreadHtml.tsx new file mode 100644 index 0000000000..43f5be7271 --- /dev/null +++ b/src/site/SW.yotsuba.Build/CatalogThreadHtml.tsx @@ -0,0 +1,25 @@ +import h, { hFragment, EscapedHtml } from "../../globals/jsx"; + +export default function generateCatalogThreadHtml( + thread, src, imgClass, data, postCount, fileCount, pageCount, staticPath, gifIcon, +): EscapedHtml { + return <> + + {imgClass ? + : + + } + +
+ + {postCount}{' / '} + {fileCount}{' / '} + {pageCount} + + + {thread.isSticky ? : ''} + {thread.isClosed ? : ''} + +
+ ; +} diff --git a/src/site/SW.yotsuba.Build/File.html b/src/site/SW.yotsuba.Build/File.html deleted file mode 100644 index 504ae42946..0000000000 --- a/src/site/SW.yotsuba.Build/File.html +++ /dev/null @@ -1,36 +0,0 @@ -?{file}{ -
- ?{boardID === "f"}{ -
- File: - ${file.name} - -(${file.size}, ${file.dimensions}?{file.tag}{, ${file.tag}}) -
- }{ -
- File: - - ?{file.isSpoiler}{Spoiler Image}{${shortFilename}} - - (${file.size}, ${file.dimensions || "PDF"}) -
- - ${file.size} - - } -
-}{ - ?{o.fileDeleted}{ -
- - File deleted. - -
- } -} diff --git a/src/site/SW.yotsuba.Build/FileHtml.tsx b/src/site/SW.yotsuba.Build/FileHtml.tsx new file mode 100644 index 0000000000..ce08287887 --- /dev/null +++ b/src/site/SW.yotsuba.Build/FileHtml.tsx @@ -0,0 +1,49 @@ +import h, { EscapedHtml, isEscaped } from "../../globals/jsx"; + +export default function generateFileHtml( + file, ID, boardID, fileURL, shortFilename, fileThumb, o, staticPath, gifIcon +): EscapedHtml { + if (file) { + const fileContent: (EscapedHtml | string)[] = []; + if (boardID === "f") { + fileContent.push( +
+ {'File: '} + {file.name} + -({file.size}, {file.dimensions}{file.tag ? ', ' + file.tag : ''}) +
+ ); + } else { + fileContent.push( +
+ {'File: '} + + {file.isSpoiler ? 'Spoiler Image' : shortFilename} + + {` (${file.size}, ${file.dimensions || "PDF"})`} +
, + + {file.size} + + ); + } + return
{...fileContent}
; + } else if (o.fileDeleted) { + return
+ + File deleted. + +
; + } + return { innerHTML: '', [isEscaped]: true }; +} diff --git a/src/site/SW.yotsuba.Build/Post.html b/src/site/SW.yotsuba.Build/Post.html deleted file mode 100644 index 655c4ea45e..0000000000 --- a/src/site/SW.yotsuba.Build/Post.html +++ /dev/null @@ -1,5 +0,0 @@ -?{o.isReply}{
>>
} -
- ?{o.isReply}{&{postInfo}&{fileBlock}}{&{fileBlock}&{postInfo}} -
&{commentHTML}
-
diff --git a/src/site/SW.yotsuba.Build/PostInfo.html b/src/site/SW.yotsuba.Build/PostInfo.html deleted file mode 100644 index 9fc75a063c..0000000000 --- a/src/site/SW.yotsuba.Build/PostInfo.html +++ /dev/null @@ -1,26 +0,0 @@ - diff --git a/src/site/SW.yotsuba.Build/PostInfoHtml.tsx b/src/site/SW.yotsuba.Build/PostInfoHtml.tsx new file mode 100644 index 0000000000..e226fb2b08 --- /dev/null +++ b/src/site/SW.yotsuba.Build/PostInfoHtml.tsx @@ -0,0 +1,91 @@ +import { g } from "../../globals/globals"; +import h, { EscapedHtml } from "../../globals/jsx"; + +export default function generatePostInfoHtml( + ID, o, subject, capcode, email, name, tripcode, pass, capcodeLC, capcodePlural, staticPath, gifIcon, + capcodeDescription, uniqueID, flag, flagCode, flagCodeTroll, dateUTC, dateText, postLink, quoteLink, boardID, + threadID, +): EscapedHtml { + const nameHtml: (EscapedHtml | string)[] = [{name}]; + if (tripcode) nameHtml.push(' ', {tripcode}); + if (pass) nameHtml.push(' ', ) + if (capcode) { + nameHtml.push( + ' ', + ## {capcode} + ) + } + + const nameBlockContent: (EscapedHtml | string)[] = + email ? [' ', {...nameHtml}] : nameHtml; + if (!(boardID === "f" && !o.isReply || capcodeDescription)) nameBlockContent.push(' '); + if (capcodeDescription) { + nameBlockContent.push( + {`${capcode} + ); + if (uniqueID && !capcode) { + nameBlockContent.push( + + (ID: ${uniqueID}) + + ) + } + } + if (flagCode) nameBlockContent.push(' ', ); + if (flagCodeTroll) nameBlockContent.push(' ', ); + + const postNumContent: (EscapedHtml | string)[] = [ + No., + {ID}, + ]; + + if (o.isSticky) { + const src = `${staticPath}sticky${gifIcon}`; + postNumContent.push(' '); + if (boardID === "f") { + postNumContent.push(Sticky); + } else { + postNumContent.push(Sticky) + } + } + if (o.isClosed && !o.isArchived) { + postNumContent.push(' '); + const src = `${staticPath}closed${gifIcon}` + if (boardID === "f") { + postNumContent.push(Closed) + } else { + postNumContent.push(Closed) + } + } + if (o.isArchived) { + postNumContent.push( + ' ', + Archived + ) + } + if (!o.isReply && g.VIEW === "index") { + // \u00A0 is nbsp + postNumContent.push(' \u00A0 ') + postNumContent.push([Reply]) + } + + return ; +} diff --git a/src/site/SW.yotsuba.js b/src/site/SW.yotsuba.js deleted file mode 100644 index 0917395135..0000000000 --- a/src/site/SW.yotsuba.js +++ /dev/null @@ -1,349 +0,0 @@ -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS205: Consider reworking code to avoid use of IIFEs - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ -SW.yotsuba = { - isOPContainerThread: false, - hasIPCount: true, - archivedBoardsKnown: true, - - urls: { - thread({boardID, threadID}) { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/thread/${threadID}`; }, - post({postID}) { return `#p${postID}`; }, - index({boardID}) { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/`; }, - catalog({boardID}) { if (boardID === 'f') { return undefined; } else { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/catalog`; } }, - archive({boardID}) { if (BoardConfig.isArchived(boardID)) { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/archive`; } else { return undefined; } }, - threadJSON({boardID, threadID}) { return `${location.protocol}//a.4cdn.org/${boardID}/thread/${threadID}.json`; }, - threadsListJSON({boardID}) { return `${location.protocol}//a.4cdn.org/${boardID}/threads.json`; }, - archiveListJSON({boardID}) { if (BoardConfig.isArchived(boardID)) { return `${location.protocol}//a.4cdn.org/${boardID}/archive.json`; } else { return ''; } }, - catalogJSON({boardID}) { return `${location.protocol}//a.4cdn.org/${boardID}/catalog.json`; }, - file({boardID}, filename) { - const hostname = boardID === 'f' ? ImageHost.flashHost() : ImageHost.host(); - return `${location.protocol}//${hostname}/${boardID}/${filename}`; - }, - thumb({boardID}, filename) { - return `${location.protocol}//${ImageHost.thumbHost()}/${boardID}/${filename}`; - } - }, - - isPrunedByAge({boardID}) { return boardID === 'f'; }, - areMD5sDeferred({boardID}) { return boardID === 'f'; }, - isOnePage({boardID}) { return boardID === 'f'; }, - noAudio({boardID}) { return BoardConfig.noAudio(boardID); }, - - selectors: { - board: '.board', - thread: '.thread', - threadDivider: '.board > hr', - summary: '.summary', - postContainer: '.postContainer', - replyOriginal: '.replyContainer:not([data-clone])', - sideArrows: 'div.sideArrows', - post: '.post', - infoRoot: '.postInfo', - info: { - subject: '.subject', - name: '.name', - email: '.useremail', - tripcode: '.postertrip', - uniqueIDRoot: '.posteruid', - uniqueID: '.posteruid > .hand', - capcode: '.capcode.hand', - pass: '.n-pu', - flag: '.flag, .bfl', - date: '.dateTime', - nameBlock: '.nameBlock', - quote: '.postNum > a:nth-of-type(2)', - reply: '.replylink' - }, - icons: { - isSticky: '.stickyIcon', - isClosed: '.closedIcon', - isArchived: '.archivedIcon' - }, - file: { - text: '.file > :first-child', - link: '.fileText > a', - thumb: 'a.fileThumb > [data-md5]' - }, - thumbLink: 'a.fileThumb', - highlightable: { - op: '.opContainer', - reply: ' > .reply', - catalog: '' - }, - comment: '.postMessage', - spoiler: 's', - quotelink: ':not(pre) > .quotelink', // XXX https://github.com/4chan/4chan-JS/issues/77: 4chan currently creates quote links inside [code] tags; ignore them - catalog: { - board: '#threads', - thread: '.thread', - thumb: '.thumb' - }, - boardList: '#boardNavDesktop > .boardList', - boardListBottom: '#boardNavDesktopFoot > .boardList', - styleSheet: 'link[title=switch]', - psa: '#globalMessage', - psaTop: '#globalToggle', - searchBox: '#search-box', - nav: { - prev: '.prev > form > [type=submit]', - next: '.next > form > [type=submit]' - } - }, - - classes: { - highlight: 'highlight' - }, - - xpath: { - thread: 'div[contains(concat(" ",@class," ")," thread ")]', - postContainer: 'div[contains(@class,"postContainer")]', - replyContainer: 'div[contains(@class,"replyContainer")]' - }, - - regexp: { - quotelink: - new RegExp(`\ -^https?://boards\\.4chan(?:nel)?\\.org/+\ -([^/]+)\ -/+thread/+\ -(\\d+)\ -(?:[/?][^#]*)?\ -(?:#p\ -(\\d+)\ -)?\ -$\ -`), - quotelinkHTML: - /]*\bhref="(?:(?:\/\/boards\.4chan(?:nel)?\.org)?\/([^\/]+)\/thread\/)?(\d+)?(?:#p(\d+))?"/g, - pass: - /^https?:\/\/www\.4chan(?:nel)?\.org\/+pass(?:$|[?#])/ - }, - - bgColoredEl() { - return $.el('div', {className: 'reply'}); - }, - - isThisPageLegit() { - // not 404 error page or similar. - return ['boards.4chan.org', 'boards.4channel.org'].includes(location.hostname) && - d.doctype && - !$('link[href*="favicon-status.ico"]', d.head) && - !['4chan - Temporarily Offline', '4chan - Error', '504 Gateway Time-out', 'MathJax Equation Source'].includes(d.title); - }, - - is404() { - // XXX Sometimes threads don't 404 but are left over as stubs containing one garbage reply post. - return ['4chan - Temporarily Offline', '4chan - 404 Not Found'].includes(d.title) || ((g.VIEW === 'thread') && $('.board') && !$('.opContainer')); - }, - - isIncomplete() { - return ['index', 'thread'].includes(g.VIEW) && !$('.board + *'); - }, - - isBoardlessPage(url) { - return ['www.4chan.org', 'www.4channel.org'].includes(url.hostname); - }, - - isAuxiliaryPage(url) { - return !['boards.4chan.org', 'boards.4channel.org'].includes(url.hostname); - }, - - isFileURL(url) { - return ImageHost.test(url.hostname); - }, - - initAuxiliary() { - switch (location.hostname) { - case 'www.4chan.org': case 'www.4channel.org': - if (SW.yotsuba.regexp.pass.test(location.href)) { - PassMessage.init(); - } else { - $.onExists(doc, 'body', () => $.addStyle(CSS.www)); - Captcha.replace.init(); - } - return; - case 'sys.4chan.org': case 'sys.4channel.org': - var pathname = location.pathname.split(/\/+/); - if (pathname[2] === 'imgboard.php') { - let match; - if (/\bmode=report\b/.test(location.search)) { - Report.init(); - } else if (match = location.search.match(/\bres=(\d+)/)) { - $.ready(function() { - if (Conf['404 Redirect'] && ($.id('errmsg')?.textContent === 'Error: Specified thread does not exist.')) { - return Redirect.navigate('thread', { - boardID: g.BOARD.ID, - postID: +match[1] - }); - }}); - } - } else if (pathname[2] === 'post') { - PostSuccessful.init(); - } - return; - } - }, - - scriptData() { - for (var script of $$('script:not([src])', d.head)) { - if (/\bcooldowns *=/.test(script.textContent)) { return script.textContent; } - } - return ''; - }, - - parseThreadMetadata(thread) { - let m; - const scriptData = this.scriptData(); - thread.postLimit = /\bbumplimit *= *1\b/.test(scriptData); - thread.fileLimit = /\bimagelimit *= *1\b/.test(scriptData); - thread.ipCount = (m = scriptData.match(/\bunique_ips *= *(\d+)\b/)) ? +m[1] : undefined; - - if ((g.BOARD.ID === 'f') && thread.OP.file) { - const {file} = thread.OP; - return $.ajax(this.urls.threadJSON({boardID: 'f', threadID: thread.ID}), { - timeout: $.MINUTE, - onloadend() { - if (this.response) { - return file.text.dataset.md5 = (file.MD5 = this.response.posts[0].md5); - } - } - } - ); - } - }, - - parseNodes(post, nodes) { - // Add CSS classes to sticky/closed icons on /f/ to match other boards. - if (post.boardID === 'f') { - return (() => { - const result = []; - for (var type of ['Sticky', 'Closed']) { - var icon; - if (icon = $(`img[alt=${type}]`, nodes.info)) { - result.push($.addClass(icon, `${type.toLowerCase()}Icon`, 'retina')); - } - } - return result; - })(); - } - }, - - parseDate(node) { - return new Date(node.dataset.utc * 1000); - }, - - parseFile(post, file) { - let info; - const {text, link, thumb} = file; - if (!(info = link.nextSibling?.textContent.match(/\(([\d.]+ [KMG]?B).*\)/))) { return false; } - $.extend(file, { - name: text.title || link.title || link.textContent, - size: info[1], - dimensions: info[0].match(/\d+x\d+/)?.[0], - tag: info[0].match(/,[^,]*, ([a-z]+)\)/i)?.[1], - MD5: text.dataset.md5 - } - ); - if (thumb) { - $.extend(file, { - thumbURL: thumb.src, - MD5: thumb.dataset.md5, - isSpoiler: $.hasClass(thumb.parentNode, 'imgspoiler') - } - ); - if (file.isSpoiler) { - let m; - file.thumbURL = (m = link.href.match(/\d+(?=\.\w+$)/)) ? `${location.protocol}//${ImageHost.thumbHost()}/${post.board}/${m[0]}s.jpg` : undefined; - } - } - return true; - }, - - cleanComment(bq) { - let abbr; - if (abbr = $('.abbr', bq)) { // 'Comment too long' or 'EXIF data available' - for (var node of $$('.abbr + br, .exif', bq)) { - $.rm(node); - } - for (let i = 0; i < 2; i++) { - var br; - if ((br = abbr.previousSibling) && (br.nodeName === 'BR')) { $.rm(br); } - } - return $.rm(abbr); - } - }, - - cleanCommentDisplay(bq) { - let b; - if ((b = $('b', bq)) && /^Rolled /.test(b.textContent)) { $.rm(b); } - return $.rm($('.fortune', bq)); - }, - - insertTags(bq) { - let node; - for (node of $$('s, .removed-spoiler', bq)) { - $.replace(node, [$.tn('[spoiler]'), ...Array.from(node.childNodes), $.tn('[/spoiler]')]); - } - for (node of $$('.prettyprint', bq)) { - $.replace(node, [$.tn('[code]'), ...Array.from(node.childNodes), $.tn('[/code]')]); - } - }, - - hasCORS(url) { - return url.split('/').slice(0, 3).join('/') === (location.protocol + '//a.4cdn.org'); - }, - - sfwBoards(sfw) { - return BoardConfig.sfwBoards(sfw); - }, - - uidColor(uid) { - let msg = 0; - let i = 0; - while (i < 8) { - msg = ((msg << 5) - msg) + uid.charCodeAt(i++); - } - return (msg >> 8) & 0xFFFFFF; - }, - - isLinkified(link) { - return ImageHost.test(link.hostname); - }, - - testNativeExtension() { - return $.global(function() { - if (window.Parser.postMenuIcon) { return this.enabled = 'true'; } - }); - }, - - transformBoardList() { - let node; - const nodes = []; - const spacer = () => $.el('span', {className: 'spacer'}); - const items = $.X('.//a|.//text()[not(ancestor::a)]', $(SW.yotsuba.selectors.boardList)); - let i = 0; - while ((node = items.snapshotItem(i++))) { - switch (node.nodeName) { - case '#text': - for (var chr of node.nodeValue) { - var span = $.el('span', {textContent: chr}); - if (chr === ' ') { span.className = 'space'; } - if (chr === ']') { nodes.push(spacer()); } - nodes.push(span); - if (chr === '[') { nodes.push(spacer()); } - } - break; - case 'A': - var a = node.cloneNode(true); - nodes.push(a); - break; - } - } - return nodes; - } -}; diff --git a/src/site/SW.yotsuba.tsx b/src/site/SW.yotsuba.tsx new file mode 100644 index 0000000000..db3f28c58d --- /dev/null +++ b/src/site/SW.yotsuba.tsx @@ -0,0 +1,718 @@ +import Redirect from "../Archive/Redirect"; +import PassMessage from "../Miscellaneous/PassMessage"; +import Report from "../Miscellaneous/Report"; +import $ from "../platform/$"; +import $$ from "../platform/$$"; +import Captcha from "../Posting/Captcha"; +import PostSuccessful from "../Posting/PostSuccessful"; +import ImageHost from "../Images/ImageHost"; +import { g, Conf, E, d, doc } from "../globals/globals"; +import BoardConfig from "../General/BoardConfig"; +import CSS from "../css/CSS"; + +import generatePostInfoHtml from './SW.yotsuba.Build/PostInfoHtml'; +import generateFileHtml from "./SW.yotsuba.Build/FileHtml"; +import generateCatalogThreadHtml from "./SW.yotsuba.Build/CatalogThreadHtml"; +import h, { hFragment, isEscaped } from "../globals/jsx"; +import { dict, MINUTE } from "../platform/helpers"; + +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS205: Consider reworking code to avoid use of IIFEs + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const SWYotsuba = { + isOPContainerThread: false, + hasIPCount: true, + archivedBoardsKnown: true, + + urls: { + thread({boardID, threadID}) { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/thread/${threadID}`; }, + post({postID}) { return `#p${postID}`; }, + index({boardID}) { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/`; }, + catalog({boardID}) { if (boardID === 'f') { return undefined; } else { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/catalog`; } }, + archive({boardID}) { if (BoardConfig.isArchived(boardID)) { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/archive`; } else { return undefined; } }, + threadJSON({boardID, threadID}) { return `${location.protocol}//a.4cdn.org/${boardID}/thread/${threadID}.json`; }, + threadsListJSON({boardID}) { return `${location.protocol}//a.4cdn.org/${boardID}/threads.json`; }, + archiveListJSON({boardID}) { if (BoardConfig.isArchived(boardID)) { return `${location.protocol}//a.4cdn.org/${boardID}/archive.json`; } else { return ''; } }, + catalogJSON({boardID}) { return `${location.protocol}//a.4cdn.org/${boardID}/catalog.json`; }, + file({boardID}, filename) { + const hostname = boardID === 'f' ? ImageHost.flashHost() : ImageHost.host(); + return `${location.protocol}//${hostname}/${boardID}/${filename}`; + }, + thumb({boardID}, filename) { + return `${location.protocol}//${ImageHost.thumbHost()}/${boardID}/${filename}`; + } + }, + + isPrunedByAge({boardID}) { return boardID === 'f'; }, + areMD5sDeferred({boardID}) { return boardID === 'f'; }, + isOnePage({boardID}) { return boardID === 'f'; }, + noAudio({boardID}) { return BoardConfig.noAudio(boardID); }, + + selectors: { + board: '.board', + thread: '.thread', + threadDivider: '.board > hr', + summary: '.summary', + postContainer: '.postContainer', + replyOriginal: '.replyContainer:not([data-clone])', + sideArrows: 'div.sideArrows', + post: '.post', + infoRoot: '.postInfo', + info: { + subject: '.subject', + name: '.name', + email: '.useremail', + tripcode: '.postertrip', + uniqueIDRoot: '.posteruid', + uniqueID: '.posteruid > .hand', + capcode: '.capcode.hand', + pass: '.n-pu', + flag: '.flag, .bfl', + date: '.dateTime', + nameBlock: '.nameBlock', + quote: '.postNum > a:nth-of-type(2)', + reply: '.replylink' + }, + icons: { + isSticky: '.stickyIcon', + isClosed: '.closedIcon', + isArchived: '.archivedIcon' + }, + file: { + text: '.file > :first-child', + link: '.fileText > a', + thumb: 'a.fileThumb > [data-md5]' + }, + thumbLink: 'a.fileThumb', + highlightable: { + op: '.opContainer', + reply: ' > .reply', + catalog: '' + }, + comment: '.postMessage', + spoiler: 's', + quotelink: ':not(pre) > .quotelink', // XXX https://github.com/4chan/4chan-JS/issues/77: 4chan currently creates quote links inside [code] tags; ignore them + catalog: { + board: '#threads', + thread: '.thread', + thumb: '.thumb' + }, + boardList: '#boardNavDesktop > .boardList', + boardListBottom: '#boardNavDesktopFoot > .boardList', + styleSheet: 'link[title=switch]', + psa: '#globalMessage', + psaTop: '#globalToggle', + searchBox: '#search-box', + nav: { + prev: '.prev > form > [type=submit]', + next: '.next > form > [type=submit]' + } + }, + + classes: { + highlight: 'highlight' + }, + + xpath: { + thread: 'div[contains(concat(" ",@class," ")," thread ")]', + postContainer: 'div[contains(@class,"postContainer")]', + replyContainer: 'div[contains(@class,"replyContainer")]' + }, + + regexp: { + quotelink: + new RegExp(`\ +^https?://boards\\.4chan(?:nel)?\\.org/+\ +([^/]+)\ +/+thread/+\ +(\\d+)\ +(?:[/?][^#]*)?\ +(?:#p\ +(\\d+)\ +)?\ +$\ +`), + quotelinkHTML: + /]*\bhref="(?:(?:\/\/boards\.4chan(?:nel)?\.org)?\/([^\/]+)\/thread\/)?(\d+)?(?:#p(\d+))?"/g, + pass: + /^https?:\/\/www\.4chan(?:nel)?\.org\/+pass(?:$|[?#])/ + }, + + bgColoredEl() { + return $.el('div', {className: 'reply'}); + }, + + isThisPageLegit() { + // not 404 error page or similar. + return ['boards.4chan.org', 'boards.4channel.org'].includes(location.hostname) && + d.doctype && + !$('link[href*="favicon-status.ico"]', d.head) && + !['4chan - Temporarily Offline', '4chan - Error', '504 Gateway Time-out', 'MathJax Equation Source'].includes(d.title); + }, + + is404() { + // XXX Sometimes threads don't 404 but are left over as stubs containing one garbage reply post. + return ['4chan - Temporarily Offline', '4chan - 404 Not Found'].includes(d.title) || ((g.VIEW === 'thread') && $('.board') && !$('.opContainer')); + }, + + isIncomplete() { + return ['index', 'thread'].includes(g.VIEW) && !$('.board + *'); + }, + + isBoardlessPage(url) { + return ['www.4chan.org', 'www.4channel.org'].includes(url.hostname); + }, + + isAuxiliaryPage(url) { + return !['boards.4chan.org', 'boards.4channel.org'].includes(url.hostname); + }, + + isFileURL(url) { + return ImageHost.test(url.hostname); + }, + + initAuxiliary() { + switch (location.hostname) { + case 'www.4chan.org': case 'www.4channel.org': + if (SWYotsuba.regexp.pass.test(location.href)) { + PassMessage.init(); + } else { + $.onExists(doc, 'body', () => $.addStyle(CSS.www)); + Captcha.replace.init(); + } + return; + case 'sys.4chan.org': case 'sys.4channel.org': + var pathname = location.pathname.split(/\/+/); + if (pathname[2] === 'imgboard.php') { + let match; + if (/\bmode=report\b/.test(location.search)) { + Report.init(); + } else if (match = location.search.match(/\bres=(\d+)/)) { + $.ready(function() { + if (Conf['404 Redirect'] && ($.id('errmsg')?.textContent === 'Error: Specified thread does not exist.')) { + return Redirect.navigate('thread', { + boardID: g.BOARD.ID, + postID: +match[1] + }); + }}); + } + } else if (pathname[2] === 'post') { + PostSuccessful.init(); + } + return; + } + }, + + scriptData() { + for (var script of $$('script:not([src])', d.head)) { + if (/\bcooldowns *=/.test(script.textContent)) { return script.textContent; } + } + return ''; + }, + + parseThreadMetadata(thread) { + let m; + const scriptData = this.scriptData(); + thread.postLimit = /\bbumplimit *= *1\b/.test(scriptData); + thread.fileLimit = /\bimagelimit *= *1\b/.test(scriptData); + thread.ipCount = (m = scriptData.match(/\bunique_ips *= *(\d+)\b/)) ? +m[1] : undefined; + + if ((g.BOARD.ID === 'f') && thread.OP.file) { + const {file} = thread.OP; + return $.ajax(this.urls.threadJSON({boardID: 'f', threadID: thread.ID}), { + timeout: MINUTE, + onloadend() { + if (this.response) { + return file.text.dataset.md5 = (file.MD5 = this.response.posts[0].md5); + } + } + } + ); + } + }, + + parseNodes(post, nodes) { + // Add CSS classes to sticky/closed icons on /f/ to match other boards. + if (post.boardID === 'f') { + return (() => { + const result = []; + for (var type of ['Sticky', 'Closed']) { + var icon; + if (icon = $(`img[alt=${type}]`, nodes.info)) { + result.push($.addClass(icon, `${type.toLowerCase()}Icon`, 'retina')); + } + } + return result; + })(); + } + }, + + parseDate(node) { + return new Date(node.dataset.utc * 1000); + }, + + parseFile(post, file) { + let info; + const {text, link, thumb} = file; + if (!(info = link.nextSibling?.textContent.match(/\(([\d.]+ [KMG]?B).*\)/))) { return false; } + $.extend(file, { + name: text.title || link.title || link.textContent, + size: info[1], + dimensions: info[0].match(/\d+x\d+/)?.[0], + tag: info[0].match(/,[^,]*, ([a-z]+)\)/i)?.[1], + MD5: text.dataset.md5 + } + ); + if (thumb) { + $.extend(file, { + thumbURL: thumb.src, + MD5: thumb.dataset.md5, + isSpoiler: $.hasClass(thumb.parentNode, 'imgspoiler') + } + ); + if (file.isSpoiler) { + let m; + file.thumbURL = (m = link.href.match(/\d+(?=\.\w+$)/)) ? `${location.protocol}//${ImageHost.thumbHost()}/${post.board}/${m[0]}s.jpg` : undefined; + } + } + return true; + }, + + cleanComment(bq) { + let abbr; + if (abbr = $('.abbr', bq)) { // 'Comment too long' or 'EXIF data available' + for (var node of $$('.abbr + br, .exif', bq)) { + $.rm(node); + } + for (let i = 0; i < 2; i++) { + var br; + if ((br = abbr.previousSibling) && (br.nodeName === 'BR')) { $.rm(br); } + } + return $.rm(abbr); + } + }, + + cleanCommentDisplay(bq) { + let b; + if ((b = $('b', bq)) && /^Rolled /.test(b.textContent)) { $.rm(b); } + return $.rm($('.fortune', bq)); + }, + + insertTags(bq) { + let node; + for (node of $$('s, .removed-spoiler', bq)) { + $.replace(node, [$.tn('[spoiler]'), ...Array.from(node.childNodes), $.tn('[/spoiler]')]); + } + for (node of $$('.prettyprint', bq)) { + $.replace(node, [$.tn('[code]'), ...Array.from(node.childNodes), $.tn('[/code]')]); + } + }, + + hasCORS(url) { + return url.split('/').slice(0, 3).join('/') === (location.protocol + '//a.4cdn.org'); + }, + + sfwBoards(sfw) { + return BoardConfig.sfwBoards(sfw); + }, + + uidColor(uid) { + let msg = 0; + let i = 0; + while (i < 8) { + msg = ((msg << 5) - msg) + uid.charCodeAt(i++); + } + return (msg >> 8) & 0xFFFFFF; + }, + + isLinkified(link) { + return ImageHost.test(link.hostname); + }, + + testNativeExtension() { + return $.global(function() { + if (window.Parser.postMenuIcon) { return this.enabled = 'true'; } + }); + }, + + transformBoardList() { + let node; + const nodes = []; + const spacer = () => $.el('span', {className: 'spacer'}); + const items = $.X('.//a|.//text()[not(ancestor::a)]', $(SW.yotsuba.selectors.boardList)); + let i = 0; + while ((node = items.snapshotItem(i++))) { + switch (node.nodeName) { + case '#text': + for (var chr of node.nodeValue) { + var span = $.el('span', {textContent: chr}); + if (chr === ' ') { span.className = 'space'; } + if (chr === ']') { nodes.push(spacer()); } + nodes.push(span); + if (chr === '[') { nodes.push(spacer()); } + } + break; + case 'A': + var a = node.cloneNode(true); + nodes.push(a); + break; + } + } + return nodes; + }, + + Build: { + staticPath: '//s.4cdn.org/image/', + gifIcon: window.devicePixelRatio >= 2 ? '@2x.gif' : '.gif', + spoilerRange: Object.create(null), + + shortFilename(filename) { + const ext = filename.match(/\.?[^\.]*$/)[0]; + if ((filename.length - ext.length) > 30) { + return `${filename.match(/(?:[\uD800-\uDBFF][\uDC00-\uDFFF]|[^]){0,25}/)[0]}(...)${ext}`; + } else { + return filename; + } + }, + + spoilerThumb(boardID) { + let spoilerRange; + if ((spoilerRange = this.spoilerRange[boardID])) { + // Randomize the spoiler image. + return `${this.staticPath}spoiler-${boardID}${Math.floor(1 + (spoilerRange * Math.random()))}.png`; + } else { + return `${this.staticPath}spoiler.png`; + } + }, + + sameThread(boardID, threadID) { + return (g.VIEW === 'thread') && (g.BOARD.ID === boardID) && (g.THREADID === +threadID); + }, + + threadURL(boardID, threadID) { + if (boardID !== g.BOARD.ID) { + return `//${BoardConfig.domain(boardID)}/${boardID}/thread/${threadID}`; + } else if ((g.VIEW !== 'thread') || (+threadID !== g.THREADID)) { + return `/${boardID}/thread/${threadID}`; + } else { + return ''; + } + }, + + postURL(boardID, threadID, postID) { + return `${this.threadURL(boardID, threadID)}#p${postID}`; + }, + + parseJSON(data, { siteID, boardID }) { + const o = { + // id + ID: data.no, + postID: data.no, + threadID: data.resto || data.no, + boardID, + siteID, + isReply: !!data.resto, + // thread status + isSticky: !!data.sticky, + isClosed: !!data.closed, + isArchived: !!data.archived, + // file status + fileDeleted: !!data.filedeleted, + filesDeleted: data.filedeleted ? [0] : [] + }; + o.info = { + subject: $.unescape(data.sub), + email: $.unescape(data.email), + name: $.unescape(data.name) || '', + tripcode: data.trip, + pass: (data.since4pass != null) ? `${data.since4pass}` : undefined, + uniqueID: data.id, + flagCode: data.country, + flagCodeTroll: data.board_flag, + flag: $.unescape((data.country_name || data.flag_name)), + dateUTC: data.time, + dateText: data.now, + // Yes, we use the raw string here + commentHTML: { innerHTML: data.com || '', [isEscaped]: true } + }; + if (data.capcode) { + o.info.capcode = data.capcode.replace(/_highlight$/, '').replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); + o.capcodeHighlight = /_highlight$/.test(data.capcode); + delete o.info.uniqueID; + } + o.files = []; + if (data.ext) { + o.file = this.parseJSONFile(data, { siteID, boardID }); + o.files.push(o.file); + } + // Temporary JSON properties for events such as April 1 / Halloween + o.extra = dict(); + for (var key in data) { + if (key[0] === 'x') { + o.extra[key] = data[key]; + } + } + return o; + }, + + parseJSONFile(data, { siteID, boardID }) { + const site = g.sites[siteID]; + const filename = (site.software === 'yotsuba') && (boardID === 'f') ? + `${encodeURIComponent(data.filename)}${data.ext}` + : + `${data.tim}${data.ext}`; + const o = { + name: ($.unescape(data.filename)) + data.ext, + url: site.urls.file({ siteID, boardID }, filename), + height: data.h, + width: data.w, + MD5: data.md5, + size: $.bytesToString(data.fsize), + thumbURL: site.urls.thumb({ siteID, boardID }, `${data.tim}s.jpg`), + theight: data.tn_h, + twidth: data.tn_w, + isSpoiler: !!data.spoiler, + tag: data.tag, + hasDownscale: !!data.m_img + }; + if ((data.h != null) && !/\.pdf$/.test(o.url)) { o.dimensions = `${o.width}x${o.height}`; } + return o; + }, + + parseComment(html) { + html = html + .replace(//gi, '\n') + .replace(/\n\n]*>/g, ''); + return $.unescape(html); + }, + + parseCommentDisplay(html) { + // Hide spoilers. + if (!Conf['Remove Spoilers'] && !Conf['Reveal Spoilers']) { + let html2; + while ((html2 = html.replace(/(?:(?!<\/?s>).)*<\/s>/g, '[spoiler]')) !== html) { + html = html2; + } + } + html = html + .replace(/^Rolled [^<]*<\/b>/i, '') // Rolls (/tg/, /qst/) + .replace(/ + {(o.isReply ?
>>
: '')} +
+ {(o.isReply ? <>{postInfo}{fileBlock} : <>{fileBlock}{postInfo})} +
{commentHTML}
+
+ ; + + const container = $.el('div', { + className: `postContainer ${postClass}Container`, + id: `pc${ID}` + }); + $.extend(container, wholePost); + + // Fix quotelinks + for (var quote of $$('.quotelink', container)) { + var href = quote.getAttribute('href'); + if (href[0] === '#') { + if (!this.sameThread(boardID, threadID)) { + quote.href = this.threadURL(boardID, threadID) + href; + } + } else { + var match; + if ((match = quote.href.match(SWYotsuba.regexp.quotelink)) && (this.sameThread(match[1], match[2]))) { + quote.href = href.match(/(#[^#]*)?$/)[0] || '#'; + } + } + } + + return container; + }, + + summaryText(status, posts, files) { + let text = ''; + if (status) { text += `${status} `; } + text += `${posts} post${posts > 1 ? 's' : ''}`; + if (+files) { text += ` and ${files} image repl${files > 1 ? 'ies' : 'y'}`; } + return text += ` ${status === '-' ? 'shown' : 'omitted'}.`; + }, + + summary(boardID, threadID, posts, files) { + return $.el('a', { + className: 'summary', + textContent: this.summaryText('', posts, files), + href: `/${boardID}/thread/${threadID}` + } + ); + }, + + thread(thread, data, withReplies) { + let root; + if (root = thread.nodes.root) { + $.rmAll(root); + } else { + thread.nodes.root = (root = $.el('div', { + className: 'thread', + id: `t${data.no}` + } + )); + } + if (this.hat) { $.add(root, this.hat.cloneNode(false)); } + $.add(root, thread.OP.nodes.root); + if (data.omitted_posts || (!withReplies && data.replies)) { + const [posts, files] = Array.from(withReplies ? + // XXX data.omitted_images is not accurate. + [data.omitted_posts, data.images - data.last_replies.filter(data => !!data.ext).length] + : + [data.replies, data.images]); + const summary = this.summary(thread.board.ID, data.no, posts, files); + $.add(root, summary); + } + return root; + }, + + catalogThread(thread, data, pageCount) { + let cssText, imgClass, src; + const { staticPath, gifIcon } = this; + const { tn_w, tn_h } = data; + + if (data.spoiler && !Conf['Reveal Spoiler Thumbnails']) { + let spoilerRange; + src = `${staticPath}spoiler`; + if (spoilerRange = this.spoilerRange[thread.board]) { + // Randomize the spoiler image. + src += (`-${thread.board}`) + Math.floor(1 + (spoilerRange * Math.random())); + } + src += '.png'; + imgClass = 'spoiler-file'; + cssText = "--tn-w: 100; --tn-h: 100;"; + } else if (data.filedeleted) { + src = `${staticPath}filedeleted-res${gifIcon}`; + imgClass = 'deleted-file'; + } else if (thread.OP.file) { + src = thread.OP.file.thumbURL; + const ratio = 250 / Math.max(tn_w, tn_h); + cssText = `--tn-w: ${tn_w * ratio}; --tn-h: ${tn_h * ratio};`; + } else { + src = `${staticPath}nofile.png`; + imgClass = 'no-file'; + } + + const postCount = data.replies + 1; + const fileCount = data.images + !!data.ext; + + const container = $.el( + 'div', + generateCatalogThreadHtml(thread, src, imgClass, data, postCount, fileCount, pageCount, staticPath, gifIcon) + ); + $.before(thread.OP.nodes.info, [...Array.from(container.childNodes)]); + + for (var br of $$('br', thread.OP.nodes.comment)) { + if (br.previousSibling && (br.previousSibling.nodeName === 'BR')) { + $.addClass(br, 'extra-linebreak'); + } + } + + const root = $.el('div', { + className: 'thread catalog-thread', + id: `t${thread}` + } + ); + if (thread.OP.highlights) { $.addClass(root, ...Array.from(thread.OP.highlights)); } + if (!thread.OP.file) { $.addClass(root, 'noFile'); } + root.style.cssText = cssText || ''; + + return root; + }, + + catalogReply(thread, data) { + let excerpt = ''; + if (data.com) { + excerpt = this.parseCommentDisplay(data.com).replace(/>>\d+/g, '').trim().replace(/\n+/g, ' // '); + } + if (data.ext) { + if (!excerpt) { excerpt = `${$.unescape(data.filename)}${data.ext}`; } + } + if (data.com) { + if (!excerpt) { excerpt = $.unescape(data.com.replace(//gi, ' // ')); } + } + if (!excerpt) { excerpt = '\xA0'; } + if (excerpt.length > 73) { excerpt = `${excerpt.slice(0, 70)}...`; } + + const link = this.postURL(thread.board.ID, thread.ID, data.no); + return $.el('div', { className: 'catalog-reply' }, + <> + : +
{excerpt} + ... + + ); + } + } +}; +export default SWYotsuba; diff --git a/src/site/Site.js b/src/site/Site.js index db70615cfc..f7bbc3ed36 100644 --- a/src/site/Site.js +++ b/src/site/Site.js @@ -1,3 +1,9 @@ +import { Conf, doc, g } from "../globals/globals"; +import Main from "../main/Main"; +import $ from "../platform/$"; +import { dict } from "../platform/helpers"; +import SW from "./SW"; + /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -26,7 +32,7 @@ var Site = { if (changes = SW[software].detect?.()) { changes.software = software; hostname = location.hostname.replace(/^www\./, ''); - var properties = (Conf['siteProperties'][hostname] || (Conf['siteProperties'][hostname] = $.dict())); + var properties = (Conf['siteProperties'][hostname] || (Conf['siteProperties'][hostname] = dict())); var changed = 0; for (var key in changes) { if (properties[key] !== changes[key]) { @@ -79,3 +85,4 @@ var Site = { return g.SITE = g.sites[hostname]; } }; +export default Site; diff --git a/src/types/customImports.d.ts b/src/types/customImports.d.ts new file mode 100644 index 0000000000..84eb6e4dcb --- /dev/null +++ b/src/types/customImports.d.ts @@ -0,0 +1,70 @@ +declare module '*.css' { const css: string; export default css; } +declare module '*.woff' { const woff: string; export default woff; } +declare module '*.woff2' { const woff2: string; export default woff2; } +declare module '*.html' { const html: string; export default html; } +declare module '*.gif' { const gif: string; export default gif; } +declare module '*.png' { const png: string; export default png; } +declare module '*.wav' { const wav: string; export default wav; } +declare module '*/package.json' { + const meta: { + "name": string, + "path": string, + "fork": string, + "page": string, + "downloads": string, + "oldVersions": string, + "faq": string, + "captchaFAQ": string, + "cssGuide": string, + "license": string, + "changelog": string, + "issues": string, + "newIssue": string, + "newIssueMaxLength": number, + "alternatives": string, + "appid": string, + "appidGecko": string, + "chromeStoreID": string, + "recaptchaKey": string, + "distBranch": string, + "includes_only": string[], + "matches_only": string[], + "matches": string[], + "matches_extra": string[], + "exclude_matches": string[], + "grants": string[], + "min": { + "chrome": string, + "firefox": string, + "greasemonkey": string + } + } + export default meta; +} +declare module '*/version.json' { + const versionInfo: { + version: string, + /** ISO */ + date: string, + } + export default versionInfo; +} +declare module '*/archives.json' { + const archives: { + uid: number, + name: string, + domain: string, + http: boolean, + https: boolean, + software: string, + boards: string[], + files: string[], + search?: string[], + reports?: boolean, + }[]; + export default archives; +} +declare module '*/banners.json' { + const banners: string[]; + export default banners; +} diff --git a/src/types/globals.d.ts b/src/types/globals.d.ts new file mode 100644 index 0000000000..2cc8ff6836 --- /dev/null +++ b/src/types/globals.d.ts @@ -0,0 +1,2 @@ +declare const cloneInto: Function; +declare const XPCNativeWrapper: any; diff --git a/src/types/jsx.d.ts b/src/types/jsx.d.ts new file mode 100644 index 0000000000..f1dc25db81 --- /dev/null +++ b/src/types/jsx.d.ts @@ -0,0 +1,6 @@ +import { EscapedHtml } from "../globals/jsx"; + +declare namespace JSX { + interface IntrinsicElements extends Record { } + interface Element extends EscapedHtml { } +} diff --git a/tools/chain.js b/tools/chain.js index e3a02e44a6..88eb2c7fbe 100644 --- a/tools/chain.js +++ b/tools/chain.js @@ -1,5 +1,5 @@ var fs = require('fs'); -var template = require('./template'); +var template = require('./template.js'); var coffee = require('coffeescript'); for (var name of process.argv.slice(2)) { diff --git a/tools/rollup-plugin-base64.js b/tools/rollup-plugin-base64.js new file mode 100644 index 0000000000..06175bfe76 --- /dev/null +++ b/tools/rollup-plugin-base64.js @@ -0,0 +1,27 @@ +import { readFile } from 'fs/promises'; +import { createFilter } from "@rollup/pluginutils"; + +/** + * @param {{ + * include: import("@rollup/pluginutils").FilterPattern, + * exclude?: import("@rollup/pluginutils").FilterPattern, + * }} opts + * @returns {import("rollup").Plugin} + */ +export default function importBase64(opts) { + if (!opts.include) { + throw Error("include option should be specified"); + } + const filter = createFilter(opts.include, opts.exclude); + + return { + name: "base64", + + async load(id) { + if (!filter(id)) return; + + const file = await readFile(id); + return `export default '${file.toString('base64')}';`; + } + }; +}; diff --git a/tools/rollup-plugin-inline-file.js b/tools/rollup-plugin-inline-file.js index df9b9c5f39..9320736861 100644 --- a/tools/rollup-plugin-inline-file.js +++ b/tools/rollup-plugin-inline-file.js @@ -1,29 +1,52 @@ -import { createFilter } from "rollup-pluginutils"; +import { createFilter } from "@rollup/pluginutils"; +import { dirname } from "path"; +import { fileURLToPath } from "url"; -export default function inlineFile(opts = {}) { - if (!opts.include) { - throw Error("include option should be specified"); - } +const __dirname = dirname(fileURLToPath(import.meta.url)); - if (opts.transformer && typeof opts.transformer !== 'function') { - throw new Error('If transformer is given, it must be a function'); - } +export default async function setupFileInliner(packageJson) { + /** @param {string} string */ + const escape = (string) => string.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\\${'); - const filter = createFilter(opts.include, opts.exclude); + /** + * @param {{ + * include: import("@rollup/pluginutils").FilterPattern, + * exclude?: import("@rollup/pluginutils").FilterPattern, + * transformer?: (input: string) => string + * wrap?: boolean + * }} opts + * @returns {import("rollup").Plugin} + */ + return function inlineFile(opts) { + if (!opts.include) { + throw Error("include option should be specified"); + } + + if (opts.transformer && typeof opts.transformer !== 'function') { + throw new Error('If transformer is given, it must be a function'); + } + + const wrap = 'wrap' in opts ? opts.wrap : true; - return { - name: "inlineFile", + const filter = createFilter(opts.include, opts.exclude); - transform(code, id) { - if (filter(id)) { - if (opts.transformer) { - code = opts.transformer(code); + return { + name: "inlineFile", + + async transform(code, id) { + if (filter(id)) { + if (opts.transformer) { + code = opts.transformer(code); + } + if (!wrap) return code; + + code = escape(code); + code = code.replace(/<%= meta\.(\w+) %>/g, (match, $1) => { + return escape(packageJson.meta[$1]); + }); + return `export default \`${code}\`;`; } - return { - code: `export default ${JSON.stringify(code)};`, - map: { mappings: "" } - }; } - } + }; }; -} \ No newline at end of file +} diff --git a/tools/rollup.js b/tools/rollup.js index 4758a3cbc2..6d58caf553 100644 --- a/tools/rollup.js +++ b/tools/rollup.js @@ -1,16 +1,96 @@ -import { rollup } from "rollup"; -import inlineFile from "./rollup-plugin-inline-file"; - -rollup({ - entry: "main.js", - plugins: [ - inlineFile({ - include: ["**/*.html", "**/*.css"], - }), - inlineFile({ - include: ["**/*.png", "**/*.gif"], - // base64 encode - transformer: btoa - }) - ] -}); \ No newline at end of file +import { rollup } from 'rollup'; +import typescript from '@rollup/plugin-typescript'; +import setupFileInliner from './rollup-plugin-inline-file.js'; +import { dirname, resolve } from 'path'; +import { fileURLToPath } from 'url'; +import generateMetadata from '../src/meta/metadata.js'; +import { copyFile, readFile, writeFile } from 'fs/promises'; +import importBase64 from './rollup-plugin-base64.js'; +import generateManifestJson from '../src/meta/manifestJson.js'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const buildDir = resolve(__dirname, '../builds/test/'); + +let channel = ''; + +if (process.argv.includes('-beta')) { + channel = '-beta'; +} else if (process.argv.includes('-noupdate')) { + channel = '-noupdate'; +} + +(async () => { + const packageJson = JSON.parse(await readFile(resolve(__dirname, '../package.json'), 'utf-8')); + + const metadata = await generateMetadata(packageJson, channel); + + const license = await readFile(resolve(__dirname, '../LICENSE'), 'utf8'); + + const version = JSON.parse(await readFile(resolve(__dirname, '../version.json'), 'utf-8')); + + + const inlineFile = await setupFileInliner(packageJson); + + const bundle = await rollup({ + input: resolve(__dirname, '../src/main/Main.js'), + plugins: [ + typescript(), + inlineFile({ + include: ["**/*.html", "**/*.css"], + }), + importBase64({ include: ["**/*.png", "**/*.gif", "**/*.wav", "**/*.woff", "**/*.woff2"] }), + inlineFile({ + include: "**/package.json", + wrap: false, + transformer(input) { + const data = JSON.parse(input); + return `export default ${JSON.stringify(data.meta, undefined, 1)};`; + } + }), + inlineFile({ + include: "**/*.json", + exclude: "**/package.json", + wrap: false, + transformer(input) { + return `export default ${input};`; + } + }) + ] + }); + + /** @type {import('rollup').OutputOptions} */ + const sharedBundleOpts = { + format: "iife", + generatedCode: { + // needed for possible circular dependencies + constBindings: false, + }, + // Can't be none as long as the root file defined exports + // exports: 'none', + }; + + // user script + await bundle.write({ + ...sharedBundleOpts, + banner: metadata + license, + // file: '../builds/test/rollupOutput.js', + file: resolve(buildDir, `${packageJson.meta.path}${channel}.user.js`), + }); + + // chrome extension + const crxDir = resolve(buildDir, 'crx'); + await bundle.write({ + ...sharedBundleOpts, + banner: license, + file: resolve(crxDir, 'script.js'), + }); + + await copyFile(resolve(__dirname, '../src/meta/eventPage.js'), resolve(crxDir, 'eventPage.js')); + + writeFile(resolve(crxDir, 'manifest.json'), generateManifestJson(packageJson, version, channel)); + + for (const file of ['icon16.png', 'icon48.png', 'icon128.png']) { + await copyFile(resolve(__dirname, '../src/meta/', file), resolve(crxDir, file)); + }; +})(); diff --git a/tools/tsconfig.json b/tools/tsconfig.json new file mode 100644 index 0000000000..b8183e413a --- /dev/null +++ b/tools/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "moduleResolution": "node16", + "types": [ + "@violentmonkey/types", + "@types/chrome", + "node" + ], + }, + "extends": "../tsconfig.json" +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..e8f32ff188 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "module": "ES2022", + "noImplicitAny": false, + "removeComments": false, + "sourceMap": true, + "target": "ES2020", + "allowJs": true, + "checkJs": true, + "noEmit": true, + "jsx": "react", + "jsxFactory": "h", + "jsxFragmentFactory": "hFragment", + "types": [ + "@violentmonkey/types", + "@types/chrome" + ], + "lib": [ + "DOM", + "ES2020" + ], + // needs to be in the deepest dir used as target in the rollup build + // https://stackoverflow.com/q/40460790, https://github.com/rollup/plugins/issues/243 + "outDir": "builds/test/crx/tsOutput", + }, + "exclude": [ + "builds/test/tsOutput" + ] +}