diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 00000000..1e6ac1a6 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,13 @@ +'use strict'; + +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +require('eslint-config-etherpad/patch/modern-module-resolution'); + +module.exports = { + root: true, + extends: 'etherpad/plugin', + ignorePatterns: [ + '/static/js/jquery.tmpl.min.js', + '/static/js/moment-with-locales.min.js', + ], +}; diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..1c4bd803 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + versioning-strategy: "increase" diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index 3cb7dad5..b80f5701 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -1,6 +1,3 @@ -# You need to change lines 38 and 46 in case the plugin's name on npmjs.com is different -# from the repository name - name: "Backend tests" # any branch is useful for testing before a PR is submitted @@ -11,40 +8,68 @@ jobs: # run on pushes to any branch # run on PRs from external forks if: | - (github.event_name != 'pull_request') - || (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id) + (github.event_name != 'pull_request') + || (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id) name: with Plugins runs-on: ubuntu-latest steps: - - name: Install libreoffice - run: | - sudo add-apt-repository -y ppa:libreoffice/ppa - sudo apt update - sudo apt install -y --no-install-recommends libreoffice libreoffice-pdfimport - - # clone etherpad-lite - - name: Install etherpad core - uses: actions/checkout@v2 - with: - repository: ether/etherpad-lite - - - name: Install all dependencies and symlink for ep_etherpad-lite - run: src/bin/installDeps.sh - - # clone this repository into node_modules/ep_plugin-name - - name: Checkout plugin repository - uses: actions/checkout@v2 - with: - path: ./node_modules/${{github.event.repository.name}} - - - name: Install plugin dependencies - run: | - cd node_modules/${{github.event.repository.name}} - npm ci - - - name: Run the backend tests - run: cd src && npm test - -##ETHERPAD_NPM_V=2 -## NPM configuration automatically created using src/bin/plugins/updateAllPluginsScript.sh + - + name: Install libreoffice + run: | + sudo add-apt-repository -y ppa:libreoffice/ppa + sudo apt update + sudo apt install -y --no-install-recommends libreoffice libreoffice-pdfimport + - + name: Install etherpad core + uses: actions/checkout@v3 + with: + repository: ether/etherpad-lite + - + name: Checkout plugin repository + uses: actions/checkout@v3 + with: + path: ./node_modules/__tmp + - + name: Determine plugin name + id: plugin_name + run: | + cd ./node_modules/__tmp + npx -c 'printf %s\\n "::set-output name=plugin_name::${npm_package_name}"' + - + name: Rename plugin directory + run: | + mv ./node_modules/__tmp ./node_modules/"${PLUGIN_NAME}" + env: + PLUGIN_NAME: ${{ steps.plugin_name.outputs.plugin_name }} + - + uses: actions/setup-node@v3 + with: + node-version: 12 + cache: 'npm' + cache-dependency-path: | + src/package-lock.json + src/bin/doc/package-lock.json + node_modules/${{ steps.plugin_name.outputs.plugin_name }}/package-lock.json + - + name: Install plugin dependencies + run: | + cd ./node_modules/"${PLUGIN_NAME}" + npm ci + env: + PLUGIN_NAME: ${{ steps.plugin_name.outputs.plugin_name }} + # Etherpad core dependencies must be installed after installing the + # plugin's dependencies, otherwise npm will try to hoist common + # dependencies by removing them from src/node_modules and installing them + # in the top-level node_modules. As of v6.14.10, npm's hoist logic appears + # to be buggy, because it sometimes removes dependencies from + # src/node_modules but fails to add them to the top-level node_modules. + # Even if npm correctly hoists the dependencies, the hoisting seems to + # confuse tools such as `npm outdated`, `npm update`, and some ESLint + # rules. + - + name: Install Etherpad core dependencies + run: src/bin/installDeps.sh + - + name: Run the backend tests + run: cd src && npm test diff --git a/.github/workflows/frontend-tests.yml b/.github/workflows/frontend-tests.yml new file mode 100644 index 00000000..8491ec3d --- /dev/null +++ b/.github/workflows/frontend-tests.yml @@ -0,0 +1,110 @@ +# Publicly credit Sauce Labs because they generously support open source +# projects. +name: "frontend tests powered by Sauce Labs" + +on: [push] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - + name: Fail if Dependabot + if: github.actor == 'dependabot[bot]' + run: | + cat <&2 + Frontend tests skipped because Dependabot can't access secrets. + Manually re-run the jobs to run the frontend tests. + For more information, see: + https://github.blog/changelog/2021-02-19-github-actions-workflows-triggered-by-dependabot-prs-will-run-with-read-only-permissions/ + EOF + exit 1 + - + name: Generate Sauce Labs strings + id: sauce_strings + run: | + printf %s\\n '::set-output name=name::${{github.event.repository.name}} ${{ github.workflow }} - ${{ github.job }}' + printf %s\\n '::set-output name=tunnel_id::${{ github.run_id }}-${{ github.run_number }}-${{ github.job }}' + - + name: Check out Etherpad core + uses: actions/checkout@v3 + with: + repository: ether/etherpad-lite + - + uses: actions/setup-node@v3 + with: + node-version: 12 + cache: 'npm' + cache-dependency-path: | + src/package-lock.json + src/bin/doc/package-lock.json + - + name: Check out the plugin + uses: actions/checkout@v3 + with: + path: ./node_modules/__tmp + - + name: export GIT_HASH to env + id: environment + run: | + cd ./node_modules/__tmp + echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})" + - + name: Determine plugin name + id: plugin_name + run: | + cd ./node_modules/__tmp + npx -c 'printf %s\\n "::set-output name=plugin_name::${npm_package_name}"' + - + name: Rename plugin directory + env: + PLUGIN_NAME: ${{ steps.plugin_name.outputs.plugin_name }} + run: | + mv ./node_modules/__tmp ./node_modules/"${PLUGIN_NAME}" + - + name: Install plugin dependencies + env: + PLUGIN_NAME: ${{ steps.plugin_name.outputs.plugin_name }} + run: | + cd ./node_modules/"${PLUGIN_NAME}" + npm ci + # Etherpad core dependencies must be installed after installing the + # plugin's dependencies, otherwise npm will try to hoist common + # dependencies by removing them from src/node_modules and installing them + # in the top-level node_modules. As of v6.14.10, npm's hoist logic appears + # to be buggy, because it sometimes removes dependencies from + # src/node_modules but fails to add them to the top-level node_modules. + # Even if npm correctly hoists the dependencies, the hoisting seems to + # confuse tools such as `npm outdated`, `npm update`, and some ESLint + # rules. + - + name: Install Etherpad core dependencies + run: src/bin/installDeps.sh + - + name: Create settings.json + run: cp settings.json.template settings.json + - + name: Disable import/export rate limiting + run: | + sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 0/' -i settings.json + - + name: Remove standard frontend test files + run: rm -rf src/tests/frontend/specs + - + uses: saucelabs/sauce-connect-action@v2.1.1 + with: + username: ${{ secrets.SAUCE_USERNAME }} + accessKey: ${{ secrets.SAUCE_ACCESS_KEY }} + tunnelIdentifier: ${{ steps.sauce_strings.outputs.tunnel_id }} + - + name: Run the frontend tests + shell: bash + env: + SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} + SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} + SAUCE_NAME: ${{ steps.sauce_strings.outputs.name }} + TRAVIS_JOB_NUMBER: ${{ steps.sauce_strings.outputs.tunnel_id }} + GIT_HASH: ${{ steps.environment.outputs.sha_short }} + run: | + src/tests/frontend/travis/runner.sh diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml index 2e0cf240..d9dfb3fe 100644 --- a/.github/workflows/npmpublish.yml +++ b/.github/workflows/npmpublish.yml @@ -21,50 +21,86 @@ jobs: # cloned to etherpad-lite then moved to ../etherpad-lite. To avoid # conflicts with this plugin's clone, etherpad-lite must be cloned and # moved out before this plugin's repo is cloned to $GITHUB_WORKSPACE. - - uses: actions/checkout@v2 + - + uses: actions/checkout@v3 with: repository: ether/etherpad-lite path: etherpad-lite - - run: mv etherpad-lite .. + - + run: mv etherpad-lite .. # etherpad-lite has been moved outside of $GITHUB_WORKSPACE, so it is now # safe to clone this plugin's repo to $GITHUB_WORKSPACE. - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - + uses: actions/checkout@v3 + # This is necessary for actions/setup-node because '..' can't be used in + # cache-dependency-path. + - + name: Create ep_etherpad-lite symlink + run: | + mkdir -p node_modules + ln -s ../../etherpad-lite/src node_modules/ep_etherpad-lite + - + uses: actions/setup-node@v3 with: node-version: 12 + cache: 'npm' + cache-dependency-path: | + node_modules/ep_etherpad-lite/package-lock.json + node_modules/ep_etherpad-lite/bin/doc/package-lock.json + package-lock.json # All of ep_etherpad-lite's devDependencies are installed because the # plugin might do `require('ep_etherpad-lite/node_modules/${devDep}')`. # Eventually it would be nice to create an ESLint plugin that prohibits # Etherpad plugins from piggybacking off of ep_etherpad-lite's # devDependencies. If we had that, we could change this line to only # install production dependencies. - - run: cd ../etherpad-lite/src && npm ci - - run: npm ci + - + run: cd ../etherpad-lite/src && npm ci + - + run: npm ci # This runs some sanity checks and creates a symlink at # node_modules/ep_etherpad-lite that points to ../../etherpad-lite/src. # This step must be done after `npm ci` installs the plugin's dependencies # because npm "helpfully" cleans up such symlinks. :( Installing # ep_etherpad-lite in the plugin's node_modules prevents lint errors and # unit test failures if the plugin does `require('ep_etherpad-lite/foo')`. - - run: npm install --no-save ep_etherpad-lite@file:../etherpad-lite/src - - run: npm test - - run: npm run lint + - + run: npm install --no-save ep_etherpad-lite@file:../etherpad-lite/src + - + run: npm test + - + run: npm run lint publish-npm: if: github.event_name == 'push' needs: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - + uses: actions/setup-node@v3 with: node-version: 12 registry-url: https://registry.npmjs.org/ - - run: git config user.name 'github-actions[bot]' - - run: git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - - run: npm ci - - run: npm version patch - - run: git push --follow-tags + cache: 'npm' + - + name: Bump version (patch) + run: | + LATEST_TAG=$(git describe --tags --abbrev=0) || exit 1 + NEW_COMMITS=$(git rev-list --count "${LATEST_TAG}"..) || exit 1 + [ "${NEW_COMMITS}" -gt 0 ] || exit 0 + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + npm ci + npm version patch + git push --follow-tags + # This is required if the package has a prepare script that uses something + # in dependencies or devDependencies. + - + run: npm ci # `npm publish` must come after `git push` otherwise there is a race # condition: If two PRs are merged back-to-back then master/main will be # updated with the commits from the second PR before the first PR's @@ -75,9 +111,12 @@ jobs: # already-used version number. By running `npm publish` after `git push`, # back-to-back merges will cause the first merge's workflow to fail but # the second's will succeed. - - run: npm publish + - + run: npm publish + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + - + name: Add package to etherpad organization + run: npm access grant read-write etherpad:developers env: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} - -##ETHERPAD_NPM_V=2 -## NPM configuration automatically created using bin/plugins/updateAllPluginsScript.sh diff --git a/.gitignore b/.gitignore index 7e9c050a..067d2fcb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ npm-debug.log node_modules .ep_initialized -node_modules/ \ No newline at end of file +node_modules/ +.idea diff --git a/commentManager.js b/commentManager.js index 1f707e34..8bbc5ff4 100644 --- a/commentManager.js +++ b/commentManager.js @@ -207,3 +207,52 @@ exports.changeCommentText = async (padId, commentId, commentText, authorId) => { // save the comment updated back await db.set(prefix + padId, comments); }; + +exports.changeComment = async (padId, commentId, commentText, changeFrom, changeTo, authorId, state) => { // eslint-disable max-len + if (commentText.length <= 0) { + logger.debug(`ignoring attempt to change comment ${commentId} to the empty string`); + throw new Error('comment_cannot_be_empty'); + } + + // Given a comment we update the comment text + + // If we're dealing with comment replies we need to a different query + let prefix = 'comments:'; + if (commentId.substring(0, 7) === 'c-reply') { + prefix = 'comment-replies:'; + } + + // get the entry + const comments = await db.get(prefix + padId); + if (comments == null || comments[commentId] == null) { + logger.debug(`ignoring attempt to edit non-existent comment ${commentId}`); + throw new Error('no_such_comment'); + } + if (comments[commentId].author !== authorId) { + logger.debug(`author ${authorId} attempted to edit comment ${commentId} ` + + `belonging to author ${comments[commentId].author}`); + throw new Error('unauth'); + } + // update the comment text + comments[commentId].text = commentText; + if (changeTo) { + comments[commentId].changeTo = changeTo; + if (!comments[commentId].changeFrom) { + comments[commentId].changeFrom = changeFrom; + } + } + + if (comments[commentId].changeTo && !changeTo) { + comments[commentId].changeTo = null; + } + + if (state) { + comments[commentId].changeAccepted = true; + comments[commentId].changeReverted = false; + } else { + comments[commentId].changeAccepted = false; + comments[commentId].changeReverted = true; + } + // save the comment updated back + await db.set(prefix + padId, comments); +}; diff --git a/exportHTML.js b/exportHTML.js index e5bf88a6..d606c159 100644 --- a/exportHTML.js +++ b/exportHTML.js @@ -16,6 +16,13 @@ exports.exportHtmlAdditionalTagsWithData = async (hookName, pad) => findAllCommentUsedOn(pad).map((name) => ['comment', name]); exports.getLineHTMLForExport = async (hookName, context) => { + const content = $('
').html(context.lineContent); + content.find('span').each(function (key, el) { + console.log(el); + const span = $(this); + const commentId = span.data('comment'); + console.log(commentId, span.html()); + }); if (settings.ep_comments_page && settings.ep_comments_page.exportHtml === false) return; // I'm not sure how optimal this is - it will do a database lookup for each line.. @@ -23,7 +30,7 @@ exports.getLineHTMLForExport = async (hookName, context) => { let hasPlugin = false; // Load the HTML into a throwaway div instead of calling $.load() to avoid // https://github.com/cheeriojs/cheerio/issues/1031 - const content = $('
').html(context.lineContent); + // const content = $('
').html(context.lineContent); // include links for each comment which we will add content later. content.find('span').each(function () { const span = $(this); diff --git a/index.js b/index.js index 0cf7a816..03ada46d 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,14 @@ 'use strict'; +const AttributePool = require('ep_etherpad-lite/static/js/AttributePool'); +const Changeset = require('ep_etherpad-lite/static/js/Changeset'); const eejs = require('ep_etherpad-lite/node/eejs/'); const settings = require('ep_etherpad-lite/node/utils/Settings'); -const formidable = require('formidable'); +const {Formidable} = require('formidable'); const commentManager = require('./commentManager'); const apiUtils = require('./apiUtils'); const _ = require('underscore'); +const padMessageHandler = require('ep_etherpad-lite/node/handler/PadMessageHandler'); const readOnlyManager = require('ep_etherpad-lite/node/db/ReadOnlyManager.js'); let io; @@ -26,13 +29,34 @@ exports.padCopy = async (hookName, context) => { ]); }; -exports.handleMessageSecurity = (hookName, context, callback) => { - const {message: {data: {apool} = {}} = {}} = context; - if (apool && apool[0] && apool[0][0] === 'comment') { - // Comment change, allow it to override readonly security model!! - return callback(true); +exports.handleMessageSecurity = async (hookName, ctx) => { + // ctx.client was renamed to ctx.socket in newer versions of Etherpad. Fall back to ctx.client in + // case this plugin is installed on an older version of Etherpad. + const {message, socket = ctx.client} = ctx; + const {type: mtype, data: {type: dtype, apool, changeset} = {}} = message; + if (mtype !== 'COLLABROOM') return; + if (dtype !== 'USER_CHANGES') return; + // Nothing needs to be done if the user already has write access. + if (!padMessageHandler.sessioninfos[socket.id].readonly) return; + const pool = new AttributePool().fromJsonable(apool); + const cs = Changeset.unpack(changeset); + const opIter = Changeset.opIterator(cs.ops); + while (opIter.hasNext()) { + const op = opIter.next(); + // Only operations that manipulate the 'comment' attribute on existing text are allowed. + if (op.opcode !== '=') return; + const forbiddenAttrib = new Error(); + try { + Changeset.eachAttribNumber(op.attribs, (n) => { + // Use an exception to break out of the iteration early. + if (pool.getAttribKey(n) !== 'comment') throw forbiddenAttrib; + }); + } catch (err) { + if (err !== forbiddenAttrib) throw err; + return; + } } - return callback(); + return true; }; exports.socketio = (hookName, args, cb) => { @@ -113,6 +137,13 @@ exports.socketio = (hookName, args, cb) => { socket.broadcast.to(padId).emit('textCommentUpdated', commentId, commentText); })); + socket.on('updateComment', handler(async (data) => { + const {commentId, commentText, authorId, changeFrom, changeTo, changeAcceptedState} = data; + const {padId} = await readOnlyManager.getIds(data.padId); + await commentManager.changeComment(padId, commentId, commentText, changeFrom, changeTo, authorId, changeAcceptedState); // eslint-disable max-len + socket.broadcast.to(padId).emit('commentUpdated', commentId, commentText, changeFrom, changeTo); // eslint-disable max-len + })); + socket.on('addCommentReply', handler(async (data) => { const {padId} = await readOnlyManager.getIds(data.padId); const [replyId, reply] = await commentManager.addCommentReply(padId, data); @@ -197,15 +228,12 @@ exports.expressCreateServer = (hookName, args, callback) => { return; } if (data == null) return; - res.json({code: 0, data}); + res.json({code: 0, message: 'ok', data}); }); args.app.post('/p/:pad/:rev?/comments', async (req, res) => { const fields = await new Promise((resolve, reject) => { - (new formidable.IncomingForm()).parse(req, (err, fields) => { - if (err != null) return reject(err); - resolve(fields); - }); + new Formidable().parse(req, (err, fields) => err ? reject(err) : resolve(fields)); }); // check the api key @@ -238,7 +266,7 @@ exports.expressCreateServer = (hookName, args, callback) => { for (let i = 0; i < commentIds.length; i++) { io.to(padIdReceived).emit('pushAddComment', commentIds[i], comments[i]); } - res.json({code: 0, commentIds}); + res.json({code: 0, message: 'ok', commentIds}); }); args.app.get('/p/:pad/:rev?/commentReplies', async (req, res) => { @@ -260,15 +288,12 @@ exports.expressCreateServer = (hookName, args, callback) => { return; } if (data == null) return; - res.json({code: 0, data}); + res.json({code: 0, message: 'ok', data}); }); args.app.post('/p/:pad/:rev?/commentReplies', async (req, res) => { const fields = await new Promise((resolve, reject) => { - (new formidable.IncomingForm()).parse(req, (err, fields) => { - if (err != null) return reject(err); - resolve(fields); - }); + new Formidable().parse(req, (err, fields) => err ? reject(err) : resolve(fields)); }); // check the api key @@ -302,7 +327,7 @@ exports.expressCreateServer = (hookName, args, callback) => { replies[i].replyId = replyIds[i]; io.to(padIdReceived).emit('pushAddCommentReply', replyIds[i], replies[i]); } - res.json({code: 0, replyIds}); + res.json({code: 0, message: 'ok', replyIds}); }); return callback(); }; diff --git a/locales/bn.json b/locales/bn.json new file mode 100644 index 00000000..e8125acb --- /dev/null +++ b/locales/bn.json @@ -0,0 +1,16 @@ +{ + "@metadata": { + "authors": [ + "আফতাবুজ্জামান" + ] + }, + "ep_comments_page.comment": "মন্তব্য", + "ep_comments_page.comments": "মন্তব্য", + "ep_comments_page.show_comments": "মন্তব্য দেখান", + "ep_comments_page.comments_template.comment.value": "মন্তব্য", + "ep_comments_page.comments_template.cancel.value": "বাতিল", + "ep_comments_page.comments_template.reply.value": "উত্তর দিন", + "ep_comments_page.comments_template.reply.placeholder": "উত্তর দিন", + "ep_comments_page.comments_template.edit_comment.save": "সংরক্ষণ", + "ep_comments_page.comments_template.edit_comment.cancel": "বাতিল" +} diff --git a/locales/da.json b/locales/da.json new file mode 100644 index 00000000..cf96b95c --- /dev/null +++ b/locales/da.json @@ -0,0 +1,32 @@ +{ + "@metadata": { + "authors": [ + "Antonla", + "Saederup92" + ] + }, + "ep_comments_page.comment": "Kommentar", + "ep_comments_page.comments": "Kommentarer", + "ep_comments_page.add_comment.title": "Tilføj ny kommentar ved markering", + "ep_comments_page.add_comment": "Tilføj ny kommentar ved markering", + "ep_comments_page.add_comment.hint": "Marker venligst først teksten for at kommentere", + "ep_comments_page.delete_comment.title": "Slet denne kommentar", + "ep_comments_page.edit_comment.title": "Rediger denne kommentar", + "ep_comments_page.show_comments": "Vis kommentarer", + "ep_comments_page.comments_template.suggested_change": "Foreslået ændring", + "ep_comments_page.comments_template.from": "Fra", + "ep_comments_page.comments_template.accept_change.value": "Godkend ændring", + "ep_comments_page.comments_template.revert_change.value": "Fortryd ændring", + "ep_comments_page.comments_template.suggested_change_from": "Foreslået ændring fra \"{{changeFrom}}\" til \"{{changeTo}}\"", + "ep_comments_page.comments_template.suggest_change_from": "Foreslå ændring fra \"{{changeFrom}}\" til", + "ep_comments_page.comments_template.to": "Til", + "ep_comments_page.comments_template.include_suggestion": "Inkluder foreslået ændring", + "ep_comments_page.comments_template.comment.value": "Kommentar", + "ep_comments_page.comments_template.cancel.value": "Annuller", + "ep_comments_page.comments_template.reply.value": "Svar", + "ep_comments_page.comments_template.reply.placeholder": "Svar", + "ep_comments_page.comments_template.edit_comment.save": "gem", + "ep_comments_page.comments_template.edit_comment.cancel": "annuller", + "ep_comments_page.error.edit_unauth": "Du kan ikke redigere andre brugeres kommentarer!", + "ep_comments_page.error.delete_unauth": "Du kan ikke slette andre brugeres kommentarer!" +} diff --git a/locales/de.json b/locales/de.json index 1591df73..d8596aa8 100644 --- a/locales/de.json +++ b/locales/de.json @@ -1,26 +1,29 @@ { - "ep_comments_page.comment" : "Kommentar", - "ep_comments_page.comments" : "Kommentare", - "ep_comments_page.add_comment.title" : "Kommentar zur Auswahl hinzufügen", - "ep_comments_page.add_comment" : "Kommentar zur Auswahl hinzufügen", - "ep_comments_page.add_comment.hint" : "Bitte wählen Sie zuerst den zu kommentierenden Text aus", - "ep_comments_page.delete_comment.title" : "Diesen Kommentar löschen", - "ep_comments_page.edit_comment.title" : "Edit this comment", - "ep_comments_page.show_comments" : "Kommentare anzeigen", - "ep_comments_page.comments_template.suggested_change" : "Vorgeschlagene Änderung", - "ep_comments_page.comments_template.from" : "von", - "ep_comments_page.comments_template.accept_change.value" : "Änderung akzeptieren", - "ep_comments_page.comments_template.revert_change.value" : "Änderung zurücknehmen", - "ep_comments_page.comments_template.suggested_change_from" : "Vorgeschlagene Änderung von", - "ep_comments_page.comments_template.suggest_change_from" : "von", - "ep_comments_page.comments_template.to" : "zu", - "ep_comments_page.comments_template.include_suggestion" : "Änderung vorschlagen", - "ep_comments_page.comments_template.comment.value" : "Kommentar", - "ep_comments_page.comments_template.cancel.value" : "Abbrechen", - "ep_comments_page.comments_template.reply.value": "Antworten", - "ep_comments_page.comments_template.reply.placeholder": "Antworten", - "ep_comments_page.comments_template.edit_comment.save" : "save", - "ep_comments_page.comments_template.edit_comment.cancel" :"cancel", - "ep_comments_page.error.edit_unauth": "You cannot edit other users comments!", - "ep_comments_page.error.delete_unauth": "You cannot delete other users comments!" + "@metadata": { + "authors": [] + }, + "ep_comments_page.comment": "Kommentar", + "ep_comments_page.comments": "Kommentare", + "ep_comments_page.add_comment.title": "Kommentar zur Auswahl hinzufügen", + "ep_comments_page.add_comment": "Kommentar zur Auswahl hinzufügen", + "ep_comments_page.add_comment.hint": "Bitte wählen Sie zuerst den zu kommentierenden Text aus", + "ep_comments_page.delete_comment.title": "Diesen Kommentar löschen", + "ep_comments_page.edit_comment.title": "Edit this comment", + "ep_comments_page.show_comments": "Kommentare anzeigen", + "ep_comments_page.comments_template.suggested_change": "Vorgeschlagene Änderung", + "ep_comments_page.comments_template.from": "von", + "ep_comments_page.comments_template.accept_change.value": "Änderung akzeptieren", + "ep_comments_page.comments_template.revert_change.value": "Änderung zurücknehmen", + "ep_comments_page.comments_template.suggested_change_from": "Vorgeschlagene Änderung von", + "ep_comments_page.comments_template.suggest_change_from": "von", + "ep_comments_page.comments_template.to": "zu", + "ep_comments_page.comments_template.include_suggestion": "Änderung vorschlagen", + "ep_comments_page.comments_template.comment.value": "Kommentar", + "ep_comments_page.comments_template.cancel.value": "Abbrechen", + "ep_comments_page.comments_template.reply.value": "Antworten", + "ep_comments_page.comments_template.reply.placeholder": "Antworten", + "ep_comments_page.comments_template.edit_comment.save": "save", + "ep_comments_page.comments_template.edit_comment.cancel": "cancel", + "ep_comments_page.error.edit_unauth": "You cannot edit other users comments!", + "ep_comments_page.error.delete_unauth": "You cannot delete other users comments!" } diff --git a/locales/diq.json b/locales/diq.json new file mode 100644 index 00000000..1a7e2584 --- /dev/null +++ b/locales/diq.json @@ -0,0 +1,14 @@ +{ + "@metadata": { + "authors": [ + "Mirzali" + ] + }, + "ep_comments_page.comment": "Mışewre", + "ep_comments_page.comments_template.comment.value": "Mışewre", + "ep_comments_page.comments_template.cancel.value": "Bıtexelne", + "ep_comments_page.comments_template.reply.value": "Cewab bıde", + "ep_comments_page.comments_template.reply.placeholder": "Cewab bıde", + "ep_comments_page.comments_template.edit_comment.save": "qeyd ke", + "ep_comments_page.comments_template.edit_comment.cancel": "bıtexelne" +} diff --git a/locales/es.json b/locales/es.json new file mode 100644 index 00000000..4b429afb --- /dev/null +++ b/locales/es.json @@ -0,0 +1,24 @@ +{ + "@metadata": { + "authors": [ + "Avengium", + "Jakeukalane" + ] + }, + "ep_comments_page.comment": "Comentario", + "ep_comments_page.comments": "Comentarios", + "ep_comments_page.add_comment.hint": "Primero selecciona el texto para comentar", + "ep_comments_page.edit_comment.title": "Editar este comentario", + "ep_comments_page.comments_template.accept_change.value": "Aceptar cambio", + "ep_comments_page.comments_template.revert_change.value": "Revertir cambio", + "ep_comments_page.comments_template.suggested_change_from": "Cambio sugerido de \"{{changeFrom}}\" a \"{{changeTo}}\"", + "ep_comments_page.comments_template.suggest_change_from": "Sugerir cambio de \"{{changeFrom}}\" a", + "ep_comments_page.comments_template.include_suggestion": "Incluir cambio sugerido", + "ep_comments_page.comments_template.comment.value": "Comentario", + "ep_comments_page.comments_template.cancel.value": "Cancelar", + "ep_comments_page.comments_template.reply.value": "Responder", + "ep_comments_page.comments_template.edit_comment.save": "Guardar", + "ep_comments_page.comments_template.edit_comment.cancel": "cancelar", + "ep_comments_page.error.edit_unauth": "¡No puedes editar los comentarios de otros usuarios!", + "ep_comments_page.error.delete_unauth": "¡No puedes eliminar los comentarios de otros usuarios!" +} diff --git a/locales/eu.json b/locales/eu.json new file mode 100644 index 00000000..4068bfb0 --- /dev/null +++ b/locales/eu.json @@ -0,0 +1,29 @@ +{ + "@metadata": { + "authors": [] + }, + "ep_comments_page.comment": "Iruzkina", + "ep_comments_page.comments": "Iruzkinak", + "ep_comments_page.add_comment.title": "Gehitu iruzkin berria hautapenari", + "ep_comments_page.add_comment": "Gehitu iruzkin berria hautapenari", + "ep_comments_page.add_comment.hint": "Aukeratu ezazu lehenengo iruzkina gehitzeko testua", + "ep_comments_page.delete_comment.title": "Ezabatu iruzkin hau", + "ep_comments_page.edit_comment.title": "Editatu iruzkin hau", + "ep_comments_page.show_comments": "Erakutsi iruzkinak", + "ep_comments_page.comments_template.suggested_change": "Proposatutako aldaketa", + "ep_comments_page.comments_template.from": "Jatorria", + "ep_comments_page.comments_template.accept_change.value": "Onartu aldaketa", + "ep_comments_page.comments_template.revert_change.value": "Leheneratu aldaketa", + "ep_comments_page.comments_template.suggested_change_from": "Proposatutako aldaketa \"{{changeFrom}}\"-(e)tik \"{{changeTo}}\"-(e)ra", + "ep_comments_page.comments_template.suggest_change_from": "Proposatu aldaketa \"{{changeFrom}}\"(e)tik hurrengo honetara:", + "ep_comments_page.comments_template.to": "Ondorengoa", + "ep_comments_page.comments_template.include_suggestion": "Gehitu proposatutako aldaketak", + "ep_comments_page.comments_template.comment.value": "Iruzkina", + "ep_comments_page.comments_template.cancel.value": "Utzi", + "ep_comments_page.comments_template.reply.value": "Erantzun", + "ep_comments_page.comments_template.reply.placeholder": "Erantzun", + "ep_comments_page.comments_template.edit_comment.save": "gorde", + "ep_comments_page.comments_template.edit_comment.cancel": "utzi", + "ep_comments_page.error.edit_unauth": "Ezin dituzu beste erabiltzaileen iruzkinak editatu!", + "ep_comments_page.error.delete_unauth": "Ezin dituzu beste erabiltzaileen iruzkinak ezabatu!" +} diff --git a/locales/fa.json b/locales/fa.json new file mode 100644 index 00000000..a9f99c7c --- /dev/null +++ b/locales/fa.json @@ -0,0 +1,29 @@ +{ + "@metadata": { + "authors": [ + "Jeeputer" + ] + }, + "ep_comments_page.comment": "توضیح", + "ep_comments_page.comments": "نظرات", + "ep_comments_page.add_comment": "افزودن توضیح تازه برای منتخب‌ها", + "ep_comments_page.delete_comment.title": "حذف این نظر", + "ep_comments_page.edit_comment.title": "ویرایش این نظر", + "ep_comments_page.show_comments": "نمایش نظرات", + "ep_comments_page.comments_template.suggested_change": "تغییر پیشنهادی", + "ep_comments_page.comments_template.from": "از", + "ep_comments_page.comments_template.accept_change.value": "پذیرفتن تغییر", + "ep_comments_page.comments_template.revert_change.value": "واگردانی تغییر", + "ep_comments_page.comments_template.suggested_change_from": "تغییر پیسنهادی از «{{changeFrom}}» به «{{changeTo}}»", + "ep_comments_page.comments_template.suggest_change_from": "تغییر پیشنهاد از «{{changeFrom}}» به", + "ep_comments_page.comments_template.to": "به", + "ep_comments_page.comments_template.include_suggestion": "گنجاندن تغییر پیشنهادی", + "ep_comments_page.comments_template.comment.value": "نظر", + "ep_comments_page.comments_template.cancel.value": "لغو", + "ep_comments_page.comments_template.reply.value": "پاسخ", + "ep_comments_page.comments_template.reply.placeholder": "پاسخ", + "ep_comments_page.comments_template.edit_comment.save": "ذخیره", + "ep_comments_page.comments_template.edit_comment.cancel": "لغو", + "ep_comments_page.error.edit_unauth": "نمی‌توانید نظرات کاربران دیگر را ویرایش کنید!", + "ep_comments_page.error.delete_unauth": "نمی‌توانید نظرات کاربران دیگر را حذف کنید!" +} diff --git a/locales/fi.json b/locales/fi.json new file mode 100644 index 00000000..5c85cc4d --- /dev/null +++ b/locales/fi.json @@ -0,0 +1,25 @@ +{ + "@metadata": { + "authors": [ + "MITO" + ] + }, + "ep_comments_page.comment": "Kommentoi", + "ep_comments_page.comments": "Kommentit", + "ep_comments_page.delete_comment.title": "Poista tämä kommentti", + "ep_comments_page.edit_comment.title": "Muokkaa tätä kommenttia", + "ep_comments_page.show_comments": "Näytä kommentit", + "ep_comments_page.comments_template.suggested_change": "Muutosehdotus", + "ep_comments_page.comments_template.from": "Lähettäjä", + "ep_comments_page.comments_template.accept_change.value": "Hyväksy muutos", + "ep_comments_page.comments_template.revert_change.value": "Peruuta muutos", + "ep_comments_page.comments_template.to": "Vastaanottaja", + "ep_comments_page.comments_template.comment.value": "Kommentoi", + "ep_comments_page.comments_template.cancel.value": "Peruuta", + "ep_comments_page.comments_template.reply.value": "Vastaa", + "ep_comments_page.comments_template.reply.placeholder": "Vastaa", + "ep_comments_page.comments_template.edit_comment.save": "tallenna", + "ep_comments_page.comments_template.edit_comment.cancel": "peruuta", + "ep_comments_page.error.edit_unauth": "Et voi muokata toisten käyttäjien kommentteja!", + "ep_comments_page.error.delete_unauth": "Et voi poistaa muiden käyttäjien kommentteja!" +} diff --git a/locales/fr.json b/locales/fr.json index 14097b04..1053c747 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -1,26 +1,31 @@ { - "ep_comments_page.comment" : "Annotation", - "ep_comments_page.comments" : "Annotations", - "ep_comments_page.add_comment.title" : "Annoter la sélection", - "ep_comments_page.add_comment" : "Annoter la sélection", - "ep_comments_page.add_comment.hint" : "Vous devez d'abord sélectionner un texte à annoter", - "ep_comments_page.delete_comment.title" : "Supprimer cette annotation", - "ep_comments_page.edit_comment.title" : "Modifier ce commentaire", - "ep_comments_page.show_comments" : "Afficher les annotations", - "ep_comments_page.comments_template.suggested_change" : "Modification proposée", - "ep_comments_page.comments_template.from" : "Remplacer", - "ep_comments_page.comments_template.accept_change.value" : "Appliquer la proposition", - "ep_comments_page.comments_template.revert_change.value" : "Annuler la proposition", - "ep_comments_page.comments_template.suggested_change_from" : "Propose de remplacer \"{{changeFrom}}\" par \"{{changeTo}}\"", - "ep_comments_page.comments_template.suggest_change_from" : "Remplacer \"{{changeFrom}}\" par", - "ep_comments_page.comments_template.to" : "Par", - "ep_comments_page.comments_template.include_suggestion" : "Proposer une modification", - "ep_comments_page.comments_template.comment.value" : "Annotation", - "ep_comments_page.comments_template.cancel.value" : "Annuler", - "ep_comments_page.comments_template.reply.value":"Répondre", - "ep_comments_page.comments_template.reply.placeholder":"Répondre", - "ep_comments_page.comments_template.edit_comment.save" : "Enregistrer", - "ep_comments_page.comments_template.edit_comment.cancel" :"Annuler", - "ep_comments_page.error.edit_unauth": "Vous ne pouvez pas modifier les commentaires des autres utilisatrices et utilisateurs !", - "ep_comments_page.error.delete_unauth": "Vous ne pouvez pas supprimer les commentaires des autres utilisatrices et utilisateurs !" + "@metadata": { + "authors": [ + "Verdy p" + ] + }, + "ep_comments_page.comment": "Annotation", + "ep_comments_page.comments": "Annotations", + "ep_comments_page.add_comment.title": "Annoter la sélection", + "ep_comments_page.add_comment": "Annoter la sélection", + "ep_comments_page.add_comment.hint": "Vous devez d'abord sélectionner un texte à annoter", + "ep_comments_page.delete_comment.title": "Supprimer cette annotation", + "ep_comments_page.edit_comment.title": "Modifier cette annotation", + "ep_comments_page.show_comments": "Afficher les annotations", + "ep_comments_page.comments_template.suggested_change": "Modification proposée", + "ep_comments_page.comments_template.from": "Remplacer", + "ep_comments_page.comments_template.accept_change.value": "Appliquer la proposition", + "ep_comments_page.comments_template.revert_change.value": "Annuler la proposition", + "ep_comments_page.comments_template.suggested_change_from": "Propose de remplacer « {{changeFrom}} » par « {{changeTo}} »", + "ep_comments_page.comments_template.suggest_change_from": "Remplacer « {{changeFrom}} » par", + "ep_comments_page.comments_template.to": "Par", + "ep_comments_page.comments_template.include_suggestion": "Inclure la modification suggérée", + "ep_comments_page.comments_template.comment.value": "Annotation", + "ep_comments_page.comments_template.cancel.value": "Annuler", + "ep_comments_page.comments_template.reply.value": "Répondre", + "ep_comments_page.comments_template.reply.placeholder": "Répondre", + "ep_comments_page.comments_template.edit_comment.save": "enregistrer", + "ep_comments_page.comments_template.edit_comment.cancel": "annuler", + "ep_comments_page.error.edit_unauth": "Vous ne pouvez pas modifier les commentaires des autres utilisatrices ou utilisateurs !", + "ep_comments_page.error.delete_unauth": "Vous ne pouvez pas supprimer les commentaires des autres utilisatrices et utilisateurs !" } diff --git a/locales/gur.json b/locales/gur.json new file mode 100644 index 00000000..0d0e4ba3 --- /dev/null +++ b/locales/gur.json @@ -0,0 +1,34 @@ +{ + "@metadata": { + "authors": [ + "Adignyoke", + "Akakiiri", + "Akoonaba", + "Amoramah" + ] + }, + "ep_comments_page.comment": "Fu yelisum", + "ep_comments_page.comments": "Lebesegɔ", + "ep_comments_page.add_comment.title": "Pa'asɛ putɛpaalɛ loisego zuo", + "ep_comments_page.add_comment": "Pa'asɛ yelesum paalega bo fu loorɛ", + "ep_comments_page.add_comment.hint": "Zaam zaam loe gɔleseko ti fu yeti fu pa'asɛ la yia", + "ep_comments_page.delete_comment.title": "Yesi lebesegɔ wã basɛ", + "ep_comments_page.edit_comment.title": "Demese sɔsekãna wa", + "ep_comments_page.show_comments": "Pa'ale fu yelesum", + "ep_comments_page.comments_template.suggested_change": "Pa'asɛ putɛ̃'ɛrɛ tee", + "ep_comments_page.comments_template.from": "Ze'ele", + "ep_comments_page.comments_template.accept_change.value": "Sakɛ Teerɛ", + "ep_comments_page.comments_template.revert_change.value": "Lebege tee", + "ep_comments_page.comments_template.suggested_change_from": "Puti'irɛ teere ze'ele\"{{tee ze'ele}}\" bo \"{{ tee bo}}\"", + "ep_comments_page.comments_template.suggest_change_from": "Pa'asɛ putɛ̃'ɛrɛ teere ze'ele{{ teere ze'ele}}", + "ep_comments_page.comments_template.to": "Wɛ̃", + "ep_comments_page.comments_template.include_suggestion": "Pa'asɛ putɛ̃'ɛrɛ teere", + "ep_comments_page.comments_template.comment.value": "Lebese", + "ep_comments_page.comments_template.cancel.value": "Sanbasɛ", + "ep_comments_page.comments_template.reply.value": "Lerege", + "ep_comments_page.comments_template.reply.placeholder": "Lerege", + "ep_comments_page.comments_template.edit_comment.save": "Seefe", + "ep_comments_page.comments_template.edit_comment.cancel": "Saabasɛ", + "ep_comments_page.error.edit_unauth": "Fu kan ta'am demese se'em sɔsega", + "ep_comments_page.error.delete_unauth": "Fu kanta'am saalum basɛba putɛra basɛ" +} diff --git a/locales/ha.json b/locales/ha.json new file mode 100644 index 00000000..6f9f0975 --- /dev/null +++ b/locales/ha.json @@ -0,0 +1,31 @@ +{ + "@metadata": { + "authors": [ + "Abbaty", + "Omar Ali", + "Salihu aliyu" + ] + }, + "ep_comments_page.comment": "Ra'ayi", + "ep_comments_page.comments": "sharhi", + "ep_comments_page.add_comment.title": "Kara sabon ra'ayi akan zaben", + "ep_comments_page.add_comment": "saka sabon bayani a bangaren", + "ep_comments_page.add_comment.hint": "zaba farkon sakon sharhi", + "ep_comments_page.delete_comment.title": "goge wannan sharhin", + "ep_comments_page.edit_comment.title": "ghera wannan sharhin", + "ep_comments_page.show_comments": "nuna sharhi", + "ep_comments_page.comments_template.suggested_change": "chanza shawara", + "ep_comments_page.comments_template.from": "daga", + "ep_comments_page.comments_template.accept_change.value": "karban chanji", + "ep_comments_page.comments_template.revert_change.value": "dawo da chanji", + "ep_comments_page.comments_template.to": "zuwa", + "ep_comments_page.comments_template.include_suggestion": "sanya damar sauyi", + "ep_comments_page.comments_template.comment.value": "Ra'ayi", + "ep_comments_page.comments_template.cancel.value": "sokewa", + "ep_comments_page.comments_template.reply.value": "Maidawa", + "ep_comments_page.comments_template.reply.placeholder": "maidawa", + "ep_comments_page.comments_template.edit_comment.save": "adanawa", + "ep_comments_page.comments_template.edit_comment.cancel": "Soke", + "ep_comments_page.error.edit_unauth": "bazaka iya gheran sharhin wani bah", + "ep_comments_page.error.delete_unauth": "bazaka iya goge sharhin wani bah" +} diff --git a/locales/he.json b/locales/he.json new file mode 100644 index 00000000..0935df68 --- /dev/null +++ b/locales/he.json @@ -0,0 +1,31 @@ +{ + "@metadata": { + "authors": [ + "Amire80" + ] + }, + "ep_comments_page.comment": "הערה", + "ep_comments_page.comments": "הערות", + "ep_comments_page.add_comment.title": "הוספת הערה חדשה לבחירה", + "ep_comments_page.add_comment": "הוספת הערה חדשה לבחירה", + "ep_comments_page.add_comment.hint": "נא לבחור קודם טקסט כדי להעיר", + "ep_comments_page.delete_comment.title": "למחוק את ההערה הזאת", + "ep_comments_page.edit_comment.title": "לערוך את ההערה הזאת", + "ep_comments_page.show_comments": "הצגת ההערה", + "ep_comments_page.comments_template.suggested_change": "שינוי מוצע", + "ep_comments_page.comments_template.from": "מאת", + "ep_comments_page.comments_template.accept_change.value": "קבלת השינוי", + "ep_comments_page.comments_template.revert_change.value": "שחזור השינוי", + "ep_comments_page.comments_template.suggested_change_from": "שינוי מוצע מהטקסט \"{{changeFrom}}\" לטקסט \"{{changeTo}}\"", + "ep_comments_page.comments_template.suggest_change_from": "להציע שינוי של הטקסט \"{{changeFrom}}\" לטקסט", + "ep_comments_page.comments_template.to": "לטקסט", + "ep_comments_page.comments_template.include_suggestion": "לכלול את השינוי המוצע", + "ep_comments_page.comments_template.comment.value": "הערה", + "ep_comments_page.comments_template.cancel.value": "ביטול", + "ep_comments_page.comments_template.reply.value": "להשיב", + "ep_comments_page.comments_template.reply.placeholder": "להשיב", + "ep_comments_page.comments_template.edit_comment.save": "שמירה", + "ep_comments_page.comments_template.edit_comment.cancel": "ביטול", + "ep_comments_page.error.edit_unauth": "אין לך אפשרות לערוך הערות של משתמשים אחרים!", + "ep_comments_page.error.delete_unauth": "אין לך אפשרות למחוק הערות של משתמשים אחרים!" +} diff --git a/locales/hi.json b/locales/hi.json new file mode 100644 index 00000000..14adf5ff --- /dev/null +++ b/locales/hi.json @@ -0,0 +1,14 @@ +{ + "@metadata": { + "authors": [ + "Abijeet Patro" + ] + }, + "ep_comments_page.comment": "टिप्पणी", + "ep_comments_page.comments": "टिप्पणियाँ", + "ep_comments_page.comments_template.cancel.value": "रद्द करें", + "ep_comments_page.comments_template.reply.value": "जवाब दें", + "ep_comments_page.comments_template.reply.placeholder": "जवाब दें", + "ep_comments_page.comments_template.edit_comment.save": "सहेजें", + "ep_comments_page.comments_template.edit_comment.cancel": "रद्द करें" +} diff --git a/locales/hu.json b/locales/hu.json index a12b40dd..3b2c7215 100644 --- a/locales/hu.json +++ b/locales/hu.json @@ -1,26 +1,29 @@ { - "ep_comments_page.comment" : "Megjegyzés", - "ep_comments_page.comments" : "Megjegyzések", - "ep_comments_page.add_comment.title" : "Új megjegyzés hozzáadása a kijelöléshez", - "ep_comments_page.add_comment" : "Új megjegyzés hozzáadása a kijelöléshez", - "ep_comments_page.add_comment.hint" : "Először jelölje meg a szöveget a megjegyzéshez", - "ep_comments_page.delete_comment.title" : "Megjegyzés törlése", - "ep_comments_page.edit_comment.title" : "Megjegyzés szerkesztése", - "ep_comments_page.show_comments" : "Megjegyzések megjelenítése", - "ep_comments_page.comments_template.suggested_change" : "Javasolt változtatás", - "ep_comments_page.comments_template.from" : "Feladó", - "ep_comments_page.comments_template.accept_change.value" : "Változtatás elfogadása", - "ep_comments_page.comments_template.revert_change.value" : "Változtatás visszavonása", - "ep_comments_page.comments_template.suggested_change_from" : "Változtatás javasolta:", - "ep_comments_page.comments_template.suggest_change_from" : "Változtatás javasolta:", - "ep_comments_page.comments_template.to" : "Címzett:", - "ep_comments_page.comments_template.include_suggestion" : "Javasolt változtatás tartalmazása", - "ep_comments_page.comments_template.comment.value" : "Megjegyzés", - "ep_comments_page.comments_template.cancel.value" : "Mégsem", - "ep_comments_page.comments_template.reply.value" : "Válasz", - "ep_comments_page.comments_template.reply.placeholder" : "Válasz", - "ep_comments_page.comments_template.edit_comment.save" : "mentés", - "ep_comments_page.comments_template.edit_comment.cancel" :"mégsem", - "ep_comments_page.error.edit_unauth": "You cannot edit other users comments!", - "ep_comments_page.error.delete_unauth": "You cannot delete other users comments!" + "@metadata": { + "authors": [] + }, + "ep_comments_page.comment": "Megjegyzés", + "ep_comments_page.comments": "Megjegyzések", + "ep_comments_page.add_comment.title": "Új megjegyzés hozzáadása a kijelöléshez", + "ep_comments_page.add_comment": "Új megjegyzés hozzáadása a kijelöléshez", + "ep_comments_page.add_comment.hint": "Először jelölje meg a szöveget a megjegyzéshez", + "ep_comments_page.delete_comment.title": "Megjegyzés törlése", + "ep_comments_page.edit_comment.title": "Megjegyzés szerkesztése", + "ep_comments_page.show_comments": "Megjegyzések megjelenítése", + "ep_comments_page.comments_template.suggested_change": "Javasolt változtatás", + "ep_comments_page.comments_template.from": "Feladó", + "ep_comments_page.comments_template.accept_change.value": "Változtatás elfogadása", + "ep_comments_page.comments_template.revert_change.value": "Változtatás visszavonása", + "ep_comments_page.comments_template.suggested_change_from": "Változtatás javasolta:", + "ep_comments_page.comments_template.suggest_change_from": "Változtatás javasolta:", + "ep_comments_page.comments_template.to": "Címzett:", + "ep_comments_page.comments_template.include_suggestion": "Javasolt változtatás tartalmazása", + "ep_comments_page.comments_template.comment.value": "Megjegyzés", + "ep_comments_page.comments_template.cancel.value": "Mégsem", + "ep_comments_page.comments_template.reply.value": "Válasz", + "ep_comments_page.comments_template.reply.placeholder": "Válasz", + "ep_comments_page.comments_template.edit_comment.save": "mentés", + "ep_comments_page.comments_template.edit_comment.cancel": "mégsem", + "ep_comments_page.error.edit_unauth": "You cannot edit other users comments!", + "ep_comments_page.error.delete_unauth": "You cannot delete other users comments!" } diff --git a/locales/io.json b/locales/io.json new file mode 100644 index 00000000..dc9beb55 --- /dev/null +++ b/locales/io.json @@ -0,0 +1,32 @@ +{ + "@metadata": { + "authors": [ + "JSantos", + "Joao Xavier" + ] + }, + "ep_comments_page.comment": "Komento", + "ep_comments_page.comments": "Komenti", + "ep_comments_page.add_comment.title": "Adjuntez nova komento en l'areo selektita", + "ep_comments_page.add_comment": "Adjuntez nova komento en l'areo selektita", + "ep_comments_page.add_comment.hint": "Selektez l'unesma texto por komentar", + "ep_comments_page.delete_comment.title": "Efacar ca komento", + "ep_comments_page.edit_comment.title": "Redaktar ca komento", + "ep_comments_page.show_comments": "Montrar komenti", + "ep_comments_page.comments_template.suggested_change": "Sugestita modifikuro", + "ep_comments_page.comments_template.from": "De", + "ep_comments_page.comments_template.accept_change.value": "Aceptar modifikuro", + "ep_comments_page.comments_template.revert_change.value": "Desfacar modifikuro", + "ep_comments_page.comments_template.suggested_change_from": "Sugestita modifikuro de \"{{changeFrom}}\" a \"{{changeTo}}\"", + "ep_comments_page.comments_template.suggest_change_from": "Modifikuro sugestita de \"{{changeFrom}}\" a", + "ep_comments_page.comments_template.to": "A", + "ep_comments_page.comments_template.include_suggestion": "Inkluzez chanjo propozata", + "ep_comments_page.comments_template.comment.value": "Komento", + "ep_comments_page.comments_template.cancel.value": "Desfacar", + "ep_comments_page.comments_template.reply.value": "Respondar", + "ep_comments_page.comments_template.reply.placeholder": "Respondo", + "ep_comments_page.comments_template.edit_comment.save": "konservez", + "ep_comments_page.comments_template.edit_comment.cancel": "desfacar", + "ep_comments_page.error.edit_unauth": "Vu ne povas redaktar la komenti di altriǃ", + "ep_comments_page.error.delete_unauth": "Vu ne povas efakar la komenti di altriǃ" +} diff --git a/locales/it.json b/locales/it.json new file mode 100644 index 00000000..2f67c846 --- /dev/null +++ b/locales/it.json @@ -0,0 +1,32 @@ +{ + "@metadata": { + "authors": [ + "Ajeje Brazorf", + "Beta16" + ] + }, + "ep_comments_page.comment": "Commento", + "ep_comments_page.comments": "Commenti", + "ep_comments_page.add_comment.title": "Aggiungi nuovo commento sulla selezione", + "ep_comments_page.add_comment": "Aggiungi nuovo commento sulla selezione", + "ep_comments_page.add_comment.hint": "Devi prima selezionare un testo su cui aggiungere il commento!", + "ep_comments_page.delete_comment.title": "Cancella questo commento", + "ep_comments_page.edit_comment.title": "Modifica questo commento", + "ep_comments_page.show_comments": "Visualizza commenti", + "ep_comments_page.comments_template.suggested_change": "Modifica suggerita", + "ep_comments_page.comments_template.from": "Da", + "ep_comments_page.comments_template.accept_change.value": "Accetta modifica", + "ep_comments_page.comments_template.revert_change.value": "Annulla modifica", + "ep_comments_page.comments_template.suggested_change_from": "Modifica suggerita da \"{{changeFrom}}\" a \"{{changeTo}}\"", + "ep_comments_page.comments_template.suggest_change_from": "Suggerisci modifica da \"{{changeFrom}}\" a", + "ep_comments_page.comments_template.to": "A", + "ep_comments_page.comments_template.include_suggestion": "Suggerisci modfica", + "ep_comments_page.comments_template.comment.value": "Commenta", + "ep_comments_page.comments_template.cancel.value": "Annulla", + "ep_comments_page.comments_template.reply.value": "Rispondi", + "ep_comments_page.comments_template.reply.placeholder": "Rispondi", + "ep_comments_page.comments_template.edit_comment.save": "salva", + "ep_comments_page.comments_template.edit_comment.cancel": "Cancella", + "ep_comments_page.error.edit_unauth": "Non puoi modificare i commenti di altri autori!", + "ep_comments_page.error.delete_unauth": "Non puoi eliminare i commenti di altri autori!" +} diff --git a/locales/ko.json b/locales/ko.json new file mode 100644 index 00000000..3a62c0ae --- /dev/null +++ b/locales/ko.json @@ -0,0 +1,30 @@ +{ + "@metadata": { + "authors": [ + "Ykhwong", + "그냥기여자" + ] + }, + "ep_comments_page.comment": "의견", + "ep_comments_page.comments": "의견", + "ep_comments_page.add_comment.title": "선택 영역에 새 댓글 추가", + "ep_comments_page.add_comment": "선택 영역에 새 댓글 추가", + "ep_comments_page.add_comment.hint": "먼저 의견을 전달할 대상 텍스트를 선택해 주십시오", + "ep_comments_page.delete_comment.title": "이 의견 삭제", + "ep_comments_page.edit_comment.title": "이 의견 편집하기", + "ep_comments_page.show_comments": "의견 표시", + "ep_comments_page.comments_template.suggested_change": "제안된 변경 사항", + "ep_comments_page.comments_template.accept_change.value": "변경 수락", + "ep_comments_page.comments_template.revert_change.value": "변경사항 되돌리기", + "ep_comments_page.comments_template.suggested_change_from": "\"{{changeFrom}}\"에서 \"{{changeTo}}\"(으)로 변경 제안됨", + "ep_comments_page.comments_template.suggest_change_from": "\"{{changeFrom}}\"에서 다음으로 변경할 것으로 제안:", + "ep_comments_page.comments_template.include_suggestion": "제안된 변경사항 포함", + "ep_comments_page.comments_template.comment.value": "의견", + "ep_comments_page.comments_template.cancel.value": "취소", + "ep_comments_page.comments_template.reply.value": "답변", + "ep_comments_page.comments_template.reply.placeholder": "답변", + "ep_comments_page.comments_template.edit_comment.save": "저장", + "ep_comments_page.comments_template.edit_comment.cancel": "취소", + "ep_comments_page.error.edit_unauth": "다른 사용자의 의견을 편집할 수 없습니다!", + "ep_comments_page.error.delete_unauth": "다른 사용자의 의견을 삭제할 수 없습니다!" +} diff --git a/locales/lb.json b/locales/lb.json new file mode 100644 index 00000000..aa0912ec --- /dev/null +++ b/locales/lb.json @@ -0,0 +1,25 @@ +{ + "@metadata": { + "authors": [ + "Robby" + ] + }, + "ep_comments_page.comment": "Bemierkung", + "ep_comments_page.comments": "Bemierkungen", + "ep_comments_page.delete_comment.title": "Dës Bemierkung läschen", + "ep_comments_page.edit_comment.title": "Dës Bemierkung änneren", + "ep_comments_page.show_comments": "Bemierkunge weisen", + "ep_comments_page.comments_template.suggested_change": "Proposéiert Ännerung", + "ep_comments_page.comments_template.from": "Vum", + "ep_comments_page.comments_template.accept_change.value": "Ännerung akzeptéieren", + "ep_comments_page.comments_template.revert_change.value": "Ännerung zrécksetzen", + "ep_comments_page.comments_template.to": "Fir", + "ep_comments_page.comments_template.comment.value": "Bemierkung", + "ep_comments_page.comments_template.cancel.value": "Ofbriechen", + "ep_comments_page.comments_template.reply.value": "Äntweren", + "ep_comments_page.comments_template.reply.placeholder": "Äntweren", + "ep_comments_page.comments_template.edit_comment.save": "späicheren", + "ep_comments_page.comments_template.edit_comment.cancel": "ofbriechen", + "ep_comments_page.error.edit_unauth": "Dir kënnt d'Bemierkunge vun anere Benotzer net änneren", + "ep_comments_page.error.delete_unauth": "Dir kënnt d'Bemierkunge vun anere Benotzer net läschen!" +} diff --git a/locales/mk.json b/locales/mk.json new file mode 100644 index 00000000..c9709087 --- /dev/null +++ b/locales/mk.json @@ -0,0 +1,31 @@ +{ + "@metadata": { + "authors": [ + "Bjankuloski06" + ] + }, + "ep_comments_page.comment": "Коментар", + "ep_comments_page.comments": "Коментари", + "ep_comments_page.add_comment.title": "Дај нов коментар за избраното", + "ep_comments_page.add_comment": "Дај нов коментар на избраното", + "ep_comments_page.add_comment.hint": "Најпрвин изберете текст за коментирање", + "ep_comments_page.delete_comment.title": "Избриши го коментаров", + "ep_comments_page.edit_comment.title": "Измени го коментаров", + "ep_comments_page.show_comments": "Прикажи коментари", + "ep_comments_page.comments_template.suggested_change": "Предложена промена", + "ep_comments_page.comments_template.from": "Од", + "ep_comments_page.comments_template.accept_change.value": "Прифати промена", + "ep_comments_page.comments_template.revert_change.value": "Отповикај промена", + "ep_comments_page.comments_template.suggested_change_from": "Предложена промена од „{{changeFrom}}“ во „{{changeTo}}“", + "ep_comments_page.comments_template.suggest_change_from": "Предложена промена од „{{changeFrom}}“ во", + "ep_comments_page.comments_template.to": "На", + "ep_comments_page.comments_template.include_suggestion": "Вклучи предложена промена", + "ep_comments_page.comments_template.comment.value": "Коментирај", + "ep_comments_page.comments_template.cancel.value": "Откажи", + "ep_comments_page.comments_template.reply.value": "Одговори", + "ep_comments_page.comments_template.reply.placeholder": "Одговори", + "ep_comments_page.comments_template.edit_comment.save": "зачувај", + "ep_comments_page.comments_template.edit_comment.cancel": "откажи", + "ep_comments_page.error.edit_unauth": "Не можеда те менувате туѓи коментари!", + "ep_comments_page.error.delete_unauth": "Не можете да бришете туѓи коментари!" +} diff --git a/locales/nl.json b/locales/nl.json index 6a08a52a..87d3e622 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -1,26 +1,31 @@ { - "ep_comments_page.comment" : "Opmerking", - "ep_comments_page.comments" : "Opmerkingen", - "ep_comments_page.add_comment.title" : "Voeg opmerking toe aan selectie", - "ep_comments_page.add_comment" : "Voeg opmerking toe aan selectie", - "ep_comments_page.add_comment.hint" : "Selecteer eerst een stuk tekst om een opmerking aan toe te voegen", - "ep_comments_page.delete_comment.title" : "Verwijder deze opmerking", - "ep_comments_page.edit_comment.title" : "Bewerk deze opmerking", - "ep_comments_page.show_comments" : "Toon opmerkingen", - "ep_comments_page.comments_template.suggested_change" : "Voorgestelde wijziging", - "ep_comments_page.comments_template.from" : "Van", - "ep_comments_page.comments_template.accept_change.value" : "Accepteer wijziging", - "ep_comments_page.comments_template.revert_change.value" : "Draai wijziging terug", - "ep_comments_page.comments_template.suggested_change_from" : "Voorgestelde wijziging van", - "ep_comments_page.comments_template.suggest_change_from" : "Voorgestelde wijziging van", - "ep_comments_page.comments_template.to" : "Naar", - "ep_comments_page.comments_template.include_suggestion" : "Voeg voorgestelde wijziging toe", - "ep_comments_page.comments_template.comment.value" : "Opmerking", - "ep_comments_page.comments_template.cancel.value" : "Annuleer", - "ep_comments_page.comments_template.reply.value" : "Antwoord", - "ep_comments_page.comments_template.reply.placeholder" : "Antwoord", - "ep_comments_page.comments_template.edit_comment.save" : "bewaar", - "ep_comments_page.comments_template.edit_comment.cancel" :"annuleer", - "ep_comments_page.error.edit_unauth": "You cannot edit other users comments!", - "ep_comments_page.error.delete_unauth": "You cannot delete other users comments!" + "@metadata": { + "authors": [ + "McDutchie" + ] + }, + "ep_comments_page.comment": "Opmerking", + "ep_comments_page.comments": "Opmerkingen", + "ep_comments_page.add_comment.title": "Voeg opmerking toe aan selectie", + "ep_comments_page.add_comment": "Voeg opmerking toe aan selectie", + "ep_comments_page.add_comment.hint": "Selecteer eerst een stuk tekst om een opmerking aan toe te voegen", + "ep_comments_page.delete_comment.title": "Verwijder deze opmerking", + "ep_comments_page.edit_comment.title": "Bewerk deze opmerking", + "ep_comments_page.show_comments": "Toon opmerkingen", + "ep_comments_page.comments_template.suggested_change": "Voorgestelde wijziging", + "ep_comments_page.comments_template.from": "Van", + "ep_comments_page.comments_template.accept_change.value": "Accepteer wijziging", + "ep_comments_page.comments_template.revert_change.value": "Draai wijziging terug", + "ep_comments_page.comments_template.suggested_change_from": "Voorgestelde wijziging van \"{{changeFrom}}\" in \"{{changeTo}}\"", + "ep_comments_page.comments_template.suggest_change_from": "Voorgestelde wijziging van \"{{changeFrom}}\" in", + "ep_comments_page.comments_template.to": "Naar", + "ep_comments_page.comments_template.include_suggestion": "Voeg voorgestelde wijziging toe", + "ep_comments_page.comments_template.comment.value": "Opmerking", + "ep_comments_page.comments_template.cancel.value": "Annuleer", + "ep_comments_page.comments_template.reply.value": "Antwoord", + "ep_comments_page.comments_template.reply.placeholder": "Antwoord", + "ep_comments_page.comments_template.edit_comment.save": "bewaar", + "ep_comments_page.comments_template.edit_comment.cancel": "annuleer", + "ep_comments_page.error.edit_unauth": "You cannot edit other users comments!", + "ep_comments_page.error.delete_unauth": "You cannot delete other users comments!" } diff --git a/locales/oc.json b/locales/oc.json new file mode 100644 index 00000000..d05386f2 --- /dev/null +++ b/locales/oc.json @@ -0,0 +1,15 @@ +{ + "@metadata": { + "authors": [ + "Quentí" + ] + }, + "ep_comments_page.comment": "Comentari", + "ep_comments_page.comments": "Comentaris", + "ep_comments_page.add_comment.title": "Apondre un comentari novèl sus la seleccion", + "ep_comments_page.comments_template.accept_change.value": "Acceptar la modificacion", + "ep_comments_page.comments_template.cancel.value": "Anullar", + "ep_comments_page.comments_template.reply.value": "Respondre", + "ep_comments_page.comments_template.edit_comment.save": "enregistrar", + "ep_comments_page.comments_template.edit_comment.cancel": "anullar" +} diff --git a/locales/pl.json b/locales/pl.json index 0c57c76f..6552ad80 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -1,26 +1,29 @@ { - "ep_comments_page.comment" : "Komentarz", - "ep_comments_page.comments" : "Komentarze", - "ep_comments_page.add_comment.title" : "Dodaj nowy komentarz do sekcji", - "ep_comments_page.add_comment" : "Dodaj nowy komentarz do sekcji", - "ep_comments_page.add_comment.hint" : "Najpierw wybierz tekst do skomentowania", - "ep_comments_page.delete_comment.title" : "Usuń komentarz", - "ep_comments_page.edit_comment.title" : "Edit this comment", - "ep_comments_page.show_comments" : "Pokaż komentarze", - "ep_comments_page.comments_template.suggested_change" : "Sugerowane zmiany", - "ep_comments_page.comments_template.from" : "Od", - "ep_comments_page.comments_template.accept_change.value" : "Zaakceptuj zmiany", - "ep_comments_page.comments_template.revert_change.value" : "Przywróc zmiany", - "ep_comments_page.comments_template.suggested_change_from" : "Sugerowana zmiana z", - "ep_comments_page.comments_template.suggest_change_from" : "Zaproponuj zmiane z", - "ep_comments_page.comments_template.to" : "Do", - "ep_comments_page.comments_template.include_suggestion" : "Dołącz sugestie", - "ep_comments_page.comments_template.comment.value" : "Komentarz", - "ep_comments_page.comments_template.cancel.value" : "Anuluj", - "ep_comments_page.comments_template.reply.value": "Odpowiedź", - "ep_comments_page.comments_template.reply.placeholder": "Odpowiedź", - "ep_comments_page.comments_template.edit_comment.save" : "save", - "ep_comments_page.comments_template.edit_comment.cancel" :"cancel", - "ep_comments_page.error.edit_unauth": "You cannot edit other users comments!", - "ep_comments_page.error.delete_unauth": "You cannot delete other users comments!" + "@metadata": { + "authors": [] + }, + "ep_comments_page.comment": "Komentarz", + "ep_comments_page.comments": "Komentarze", + "ep_comments_page.add_comment.title": "Dodaj nowy komentarz do sekcji", + "ep_comments_page.add_comment": "Dodaj nowy komentarz do sekcji", + "ep_comments_page.add_comment.hint": "Najpierw wybierz tekst do skomentowania", + "ep_comments_page.delete_comment.title": "Usuń komentarz", + "ep_comments_page.edit_comment.title": "Edit this comment", + "ep_comments_page.show_comments": "Pokaż komentarze", + "ep_comments_page.comments_template.suggested_change": "Sugerowane zmiany", + "ep_comments_page.comments_template.from": "Od", + "ep_comments_page.comments_template.accept_change.value": "Zaakceptuj zmiany", + "ep_comments_page.comments_template.revert_change.value": "Przywróc zmiany", + "ep_comments_page.comments_template.suggested_change_from": "Sugerowana zmiana z", + "ep_comments_page.comments_template.suggest_change_from": "Zaproponuj zmiane z", + "ep_comments_page.comments_template.to": "Do", + "ep_comments_page.comments_template.include_suggestion": "Dołącz sugestie", + "ep_comments_page.comments_template.comment.value": "Komentarz", + "ep_comments_page.comments_template.cancel.value": "Anuluj", + "ep_comments_page.comments_template.reply.value": "Odpowiedź", + "ep_comments_page.comments_template.reply.placeholder": "Odpowiedź", + "ep_comments_page.comments_template.edit_comment.save": "save", + "ep_comments_page.comments_template.edit_comment.cancel": "cancel", + "ep_comments_page.error.edit_unauth": "You cannot edit other users comments!", + "ep_comments_page.error.delete_unauth": "You cannot delete other users comments!" } diff --git a/locales/pt-br.json b/locales/pt-br.json new file mode 100644 index 00000000..0e48c8c1 --- /dev/null +++ b/locales/pt-br.json @@ -0,0 +1,31 @@ +{ + "@metadata": { + "authors": [ + "Eduardoaddad", + "Svjatysberega" + ] + }, + "ep_comments_page.comment": "Comentário", + "ep_comments_page.comments": "Comentários", + "ep_comments_page.add_comment.title": "Adicionar um novo comentário sobre a seleção", + "ep_comments_page.add_comment.hint": "Favor selecionar primeiro o texto a comentar", + "ep_comments_page.delete_comment.title": "Apagar este comentário", + "ep_comments_page.edit_comment.title": "Editar este comentário", + "ep_comments_page.show_comments": "Mostrar comentários", + "ep_comments_page.comments_template.suggested_change": "Alteração Sugerida", + "ep_comments_page.comments_template.from": "De", + "ep_comments_page.comments_template.accept_change.value": "Aceitar Alteração", + "ep_comments_page.comments_template.revert_change.value": "Reverter Alteração", + "ep_comments_page.comments_template.suggested_change_from": "Alteração sugerida de \"{{changeFrom}}\" para \"{{changeTo}}\"", + "ep_comments_page.comments_template.suggest_change_from": "Sugerir alteração de \"{{changeFrom}}\" para", + "ep_comments_page.comments_template.to": "Para", + "ep_comments_page.comments_template.include_suggestion": "Incluir alteração sugerida", + "ep_comments_page.comments_template.comment.value": "Comentário", + "ep_comments_page.comments_template.cancel.value": "Cancelar", + "ep_comments_page.comments_template.reply.value": "Responder", + "ep_comments_page.comments_template.reply.placeholder": "Responder", + "ep_comments_page.comments_template.edit_comment.save": "salvar", + "ep_comments_page.comments_template.edit_comment.cancel": "cancelar", + "ep_comments_page.error.edit_unauth": "Não é possível editar comentários de outros usuários!", + "ep_comments_page.error.delete_unauth": "Não é possível apagar comentários de outros usuários!" +} diff --git a/locales/qqq.json b/locales/qqq.json index 39ecf34c..f191fdc7 100644 --- a/locales/qqq.json +++ b/locales/qqq.json @@ -1,18 +1,9 @@ { - "ep_comments_page.time.seconds.past": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.seconds.future": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.minutes.past": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.minutes.future": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.hours.past": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.hours.future": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.days.past": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.days.future": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.weeks.past": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.weeks.future": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.months.past": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.months.future": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.years.past": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.years.future": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.centuries.past": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro", - "ep_comments_page.time.centuries.future": "See https://github.com/marcelklehr/html10n.js#pluralization for how to use the 'plural' macro" + "@metadata": { + "authors": [ + "Ajeje Brazorf" + ] + }, + "ep_comments_page.comments_template.cancel.value": "{{Identical|Cancel}}", + "ep_comments_page.comments_template.edit_comment.cancel": "{{Identical|Cancel}}" } diff --git a/locales/ru.json b/locales/ru.json index 77f619d9..279d2fe5 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1,26 +1,32 @@ { - "ep_comments_page.comment" : "Примечание", - "ep_comments_page.comments" : "Примечания", - "ep_comments_page.add_comment.title" : "Добавьте примечание к выделенному тексту", - "ep_comments_page.add_comment" : "Добавьте примечание к выделенному тексту", - "ep_comments_page.add_comment.hint" : "Выделите текст чтобы создать примечание", - "ep_comments_page.delete_comment.title" : "Удалить примечание", - "ep_comments_page.edit_comment.title" : "Отредактировать примечание", - "ep_comments_page.show_comments" : "Показывать примечания", - "ep_comments_page.comments_template.suggested_change" : "Предлагаемое изменение", - "ep_comments_page.comments_template.from" : "Заменить", - "ep_comments_page.comments_template.accept_change.value" : "Принять изменение", - "ep_comments_page.comments_template.revert_change.value" : "Отменить изменение", - "ep_comments_page.comments_template.suggested_change_from" : "Предложено заменить", - "ep_comments_page.comments_template.suggest_change_from" : "Заменить", - "ep_comments_page.comments_template.to" : "на", - "ep_comments_page.comments_template.include_suggestion" : "Предложить правку", - "ep_comments_page.comments_template.comment.value" : "Отправить", - "ep_comments_page.comments_template.cancel.value" : "Отменить", - "ep_comments_page.comments_template.reply.value" : "Ответить", - "ep_comments_page.comments_template.reply.placeholder" : "Ответить", - "ep_comments_page.comments_template.edit_comment.save" : "сохранить", - "ep_comments_page.comments_template.edit_comment.cancel" :"отменить", - "ep_comments_page.error.edit_unauth": "You cannot edit other users comments!", - "ep_comments_page.error.delete_unauth": "You cannot delete other users comments!" + "@metadata": { + "authors": [ + "DDPAT", + "Ice bulldog" + ] + }, + "ep_comments_page.comment": "Примечание", + "ep_comments_page.comments": "Примечания", + "ep_comments_page.add_comment.title": "Добавьте примечание к выделенному тексту", + "ep_comments_page.add_comment": "Добавьте примечание к выделенному тексту", + "ep_comments_page.add_comment.hint": "Выделите текст чтобы создать примечание", + "ep_comments_page.delete_comment.title": "Удалить примечание", + "ep_comments_page.edit_comment.title": "Отредактировать примечание", + "ep_comments_page.show_comments": "Показывать примечания", + "ep_comments_page.comments_template.suggested_change": "Предлагаемое изменение", + "ep_comments_page.comments_template.from": "Заменить", + "ep_comments_page.comments_template.accept_change.value": "Принять изменение", + "ep_comments_page.comments_template.revert_change.value": "Отменить изменение", + "ep_comments_page.comments_template.suggested_change_from": "Предлагаемое изменение с «{{changeFrom}}» на «{{changeTo}}»", + "ep_comments_page.comments_template.suggest_change_from": "Предложить изменение с «{{changeFrom}}» на", + "ep_comments_page.comments_template.to": "на", + "ep_comments_page.comments_template.include_suggestion": "Предложить правку", + "ep_comments_page.comments_template.comment.value": "Отправить", + "ep_comments_page.comments_template.cancel.value": "Отменить", + "ep_comments_page.comments_template.reply.value": "Ответить", + "ep_comments_page.comments_template.reply.placeholder": "Ответить", + "ep_comments_page.comments_template.edit_comment.save": "сохранить", + "ep_comments_page.comments_template.edit_comment.cancel": "отменить", + "ep_comments_page.error.edit_unauth": "You cannot edit other users comments!", + "ep_comments_page.error.delete_unauth": "You cannot delete other users comments!" } diff --git a/locales/scn.json b/locales/scn.json new file mode 100644 index 00000000..461c517a --- /dev/null +++ b/locales/scn.json @@ -0,0 +1,16 @@ +{ + "@metadata": { + "authors": [ + "Ajeje Brazorf" + ] + }, + "ep_comments_page.comment": "Cummentu", + "ep_comments_page.comments": "Cummenti", + "ep_comments_page.delete_comment.title": "Cancella stu cummento", + "ep_comments_page.edit_comment.title": "Cancia stu cummentu", + "ep_comments_page.show_comments": "Ammustra cummenti", + "ep_comments_page.comments_template.comment.value": "Cummentu", + "ep_comments_page.comments_template.cancel.value": "Annulla", + "ep_comments_page.comments_template.edit_comment.save": "sarva", + "ep_comments_page.comments_template.edit_comment.cancel": "annulla" +} diff --git a/locales/sk.json b/locales/sk.json new file mode 100644 index 00000000..328f9251 --- /dev/null +++ b/locales/sk.json @@ -0,0 +1,31 @@ +{ + "@metadata": { + "authors": [ + "Yardom78" + ] + }, + "ep_comments_page.comment": "Komentár", + "ep_comments_page.comments": "Komentáre", + "ep_comments_page.add_comment.title": "Pridať nový komentár", + "ep_comments_page.add_comment": "Pridať nový komentár", + "ep_comments_page.add_comment.hint": "Prosím vyberte text na komentár", + "ep_comments_page.delete_comment.title": "Vymazať tento komentár", + "ep_comments_page.edit_comment.title": "Upraviť tento komentár", + "ep_comments_page.show_comments": "Zobraziť komentáre", + "ep_comments_page.comments_template.suggested_change": "Navrhovaná zmena", + "ep_comments_page.comments_template.from": "Od", + "ep_comments_page.comments_template.accept_change.value": "Súhlasiť so zmenou", + "ep_comments_page.comments_template.revert_change.value": "Vrátiť zmenu späť", + "ep_comments_page.comments_template.suggested_change_from": "Navrhované zmeny od \"{{changeFrom}}\" do \"{{changeTo}}\"", + "ep_comments_page.comments_template.suggest_change_from": "Navrhnúť zmeny od \"{{changeFrom}}\" do", + "ep_comments_page.comments_template.to": "Komu", + "ep_comments_page.comments_template.include_suggestion": "Zahrnúť navrhovanú zmenu", + "ep_comments_page.comments_template.comment.value": "Komentár", + "ep_comments_page.comments_template.cancel.value": "Zrušiť", + "ep_comments_page.comments_template.reply.value": "Odpovedať", + "ep_comments_page.comments_template.reply.placeholder": "Odpovedať", + "ep_comments_page.comments_template.edit_comment.save": "uložiť", + "ep_comments_page.comments_template.edit_comment.cancel": "zrušiť", + "ep_comments_page.error.edit_unauth": "Nemôžete upravovať komentáre iných používateľov!", + "ep_comments_page.error.delete_unauth": "Nemôžete vymazať komentáre iných používateľov!" +} diff --git a/locales/sv.json b/locales/sv.json index d43af37b..39527cf7 100644 --- a/locales/sv.json +++ b/locales/sv.json @@ -1,26 +1,31 @@ { - "ep_comments_page.comment" : "Kommentar", - "ep_comments_page.comments" : "Kommentarer", - "ep_comments_page.add_comment.title" : "Lägg till ny kommentar till markering", - "ep_comments_page.add_comment" : "Lägg till ny kommentar till markering", - "ep_comments_page.add_comment.hint" : "Markera först den text du vill kommentera", - "ep_comments_page.delete_comment.title" : "Radera den här kommentaren", - "ep_comments_page.edit_comment.title" : "Redigera den här kommentaren", - "ep_comments_page.show_comments" : "Visa kommentarer", - "ep_comments_page.comments_template.suggested_change" : "Föreslagen ändring", - "ep_comments_page.comments_template.from" : "Från", - "ep_comments_page.comments_template.accept_change.value" : "Godkänn ändringsförslag", - "ep_comments_page.comments_template.revert_change.value" : "Avslå ändringsförslag", - "ep_comments_page.comments_template.suggested_change_from" : "Förslag att ändra från", - "ep_comments_page.comments_template.suggest_change_from" : "Föreslå att ändring från", - "ep_comments_page.comments_template.to" : "Till", - "ep_comments_page.comments_template.include_suggestion" : "Bifoga ändringsförslag", - "ep_comments_page.comments_template.comment.value" : "Kommentera", - "ep_comments_page.comments_template.cancel.value" : "Avbryt", - "ep_comments_page.comments_template.reply.value" : "Svara", - "ep_comments_page.comments_template.reply.placeholder" : "Svar", - "ep_comments_page.comments_template.edit_comment.save" : "spara", - "ep_comments_page.comments_template.edit_comment.cancel" :"avbryt", - "ep_comments_page.error.edit_unauth": "You cannot edit other users comments!", - "ep_comments_page.error.delete_unauth": "You cannot delete other users comments!" + "@metadata": { + "authors": [ + "Sabelöga" + ] + }, + "ep_comments_page.comment": "Kommentar", + "ep_comments_page.comments": "Kommentarer", + "ep_comments_page.add_comment.title": "Lägg till ny kommentar till markering", + "ep_comments_page.add_comment": "Lägg till ny kommentar till markering", + "ep_comments_page.add_comment.hint": "Markera först den text du vill kommentera", + "ep_comments_page.delete_comment.title": "Radera den här kommentaren", + "ep_comments_page.edit_comment.title": "Redigera den här kommentaren", + "ep_comments_page.show_comments": "Visa kommentarer", + "ep_comments_page.comments_template.suggested_change": "Föreslagen ändring", + "ep_comments_page.comments_template.from": "Från", + "ep_comments_page.comments_template.accept_change.value": "Godkänn ändring", + "ep_comments_page.comments_template.revert_change.value": "Ångra ändring", + "ep_comments_page.comments_template.suggested_change_from": "Föreslagen ändring från \"{{changeFrom}}\" till \"{{changeTo}}\"", + "ep_comments_page.comments_template.suggest_change_from": "Föreslå ändring från \"{{changeFrom}}\" till", + "ep_comments_page.comments_template.to": "Till", + "ep_comments_page.comments_template.include_suggestion": "Bifoga ändringsförslag", + "ep_comments_page.comments_template.comment.value": "Kommentera", + "ep_comments_page.comments_template.cancel.value": "Avbryt", + "ep_comments_page.comments_template.reply.value": "Svara", + "ep_comments_page.comments_template.reply.placeholder": "Svar", + "ep_comments_page.comments_template.edit_comment.save": "spara", + "ep_comments_page.comments_template.edit_comment.cancel": "avbryt", + "ep_comments_page.error.edit_unauth": "Du kan inte redigera andra användares kommentarer!", + "ep_comments_page.error.delete_unauth": "Du kan inte radera andra användares kommentarer!" } diff --git a/locales/th.json b/locales/th.json new file mode 100644 index 00000000..59a1d222 --- /dev/null +++ b/locales/th.json @@ -0,0 +1,32 @@ +{ + "@metadata": { + "authors": [ + "Prame Tan", + "Thas Tayapongsak" + ] + }, + "ep_comments_page.comment": "ความคิดเห็น", + "ep_comments_page.comments": "ความเห็น", + "ep_comments_page.add_comment.title": "เพิ่มความเห็นสำหรับส่วนที่เลือก", + "ep_comments_page.add_comment": "เพิ่มความเห็นสำหรับส่วนที่เลือก", + "ep_comments_page.add_comment.hint": "กรุณาเลือกข้อความที่จะแสดงความเห็น", + "ep_comments_page.delete_comment.title": "ลบความเห็น", + "ep_comments_page.edit_comment.title": "แก้ไขความคิดเห็น", + "ep_comments_page.show_comments": "แสดงความเห็น", + "ep_comments_page.comments_template.suggested_change": "การเปลี่ยนแปลงที่เสนอ", + "ep_comments_page.comments_template.from": "จาก", + "ep_comments_page.comments_template.accept_change.value": "ยอมรับการเปลี่ยนแปลง", + "ep_comments_page.comments_template.revert_change.value": "ย้อนคืนการเปลี่ยนแปลง", + "ep_comments_page.comments_template.suggested_change_from": "เสนอให้เปลี่ยน \"{{changeFrom}}\" ไปเป็น \"{{changeTo}}\"", + "ep_comments_page.comments_template.suggest_change_from": "เสนอให้เปลี่ยนจาก \"{{changeFrom}}\" ไปเป็น", + "ep_comments_page.comments_template.to": "ถึง", + "ep_comments_page.comments_template.include_suggestion": "เพิ่มการเปลี่ยนแปลงที่เสนอ", + "ep_comments_page.comments_template.comment.value": "ความเห็น", + "ep_comments_page.comments_template.cancel.value": "ยกเลิก", + "ep_comments_page.comments_template.reply.value": "ตอบกลับ", + "ep_comments_page.comments_template.reply.placeholder": "ตอบกลับ", + "ep_comments_page.comments_template.edit_comment.save": "บันทึก", + "ep_comments_page.comments_template.edit_comment.cancel": "ยกเลิก", + "ep_comments_page.error.edit_unauth": "คุณไม่สามารถแก้ไขความเห็นของผู้ใช้อื่นได้", + "ep_comments_page.error.delete_unauth": "คุณไม่สามารถลบความเห็นของผู้ใช้อื่นได้" +} diff --git a/locales/tr.json b/locales/tr.json new file mode 100644 index 00000000..ca7b23ca --- /dev/null +++ b/locales/tr.json @@ -0,0 +1,33 @@ +{ + "@metadata": { + "authors": [ + "Can", + "Erdemkose", + "Hedda" + ] + }, + "ep_comments_page.comment": "Yorum", + "ep_comments_page.comments": "Yorumlar", + "ep_comments_page.add_comment.title": "Seçime yeni yorum ekle", + "ep_comments_page.add_comment": "Seçime yeni yorum ekle", + "ep_comments_page.add_comment.hint": "Lütfen önce yorum yapılacak metni seçin", + "ep_comments_page.delete_comment.title": "Bu yorumu sil", + "ep_comments_page.edit_comment.title": "Bu yorumu düzenle", + "ep_comments_page.show_comments": "Yorumları göster", + "ep_comments_page.comments_template.suggested_change": "Önerilen Değişiklik", + "ep_comments_page.comments_template.from": "Gönderen", + "ep_comments_page.comments_template.accept_change.value": "Değişikliği Kabul Et", + "ep_comments_page.comments_template.revert_change.value": "Değişikliği Geri Al", + "ep_comments_page.comments_template.suggested_change_from": "\"{{changeFrom}}\"'dan/den, {{changeTo}}'ye/ya önerilen değişiklik", + "ep_comments_page.comments_template.suggest_change_from": "\"{{changeFrom}}\"'den/dan şuraya önerilen değişiklik", + "ep_comments_page.comments_template.to": "Alıcı", + "ep_comments_page.comments_template.include_suggestion": "Önerilen değişikliği dahil et", + "ep_comments_page.comments_template.comment.value": "Yorum", + "ep_comments_page.comments_template.cancel.value": "İptal", + "ep_comments_page.comments_template.reply.value": "Yanıtla", + "ep_comments_page.comments_template.reply.placeholder": "Yanıtla", + "ep_comments_page.comments_template.edit_comment.save": "kaydet", + "ep_comments_page.comments_template.edit_comment.cancel": "iptal", + "ep_comments_page.error.edit_unauth": "Diğer kullanıcıların yorumlarını düzenleyemezsiniz!", + "ep_comments_page.error.delete_unauth": "Diğer kullanıcıların yorumlarını silemezsiniz!" +} diff --git a/locales/uk.json b/locales/uk.json new file mode 100644 index 00000000..7dacf815 --- /dev/null +++ b/locales/uk.json @@ -0,0 +1,32 @@ +{ + "@metadata": { + "authors": [ + "DDPAT", + "Ice bulldog" + ] + }, + "ep_comments_page.comment": "Коментар", + "ep_comments_page.comments": "Коментарі", + "ep_comments_page.add_comment.title": "Додати новий коментар до вибору", + "ep_comments_page.add_comment": "Додати новий коментар до вибору", + "ep_comments_page.add_comment.hint": "Будь ласка, спочатку виберіть текст для коментування", + "ep_comments_page.delete_comment.title": "Видалити цей коментар", + "ep_comments_page.edit_comment.title": "Редагувати цей коментар", + "ep_comments_page.show_comments": "Показати коментарі", + "ep_comments_page.comments_template.suggested_change": "Запропонована зміна", + "ep_comments_page.comments_template.from": "Від", + "ep_comments_page.comments_template.accept_change.value": "Прийняти зміну", + "ep_comments_page.comments_template.revert_change.value": "Скасувати зміни", + "ep_comments_page.comments_template.suggested_change_from": "Пропонована зміна з «{{changeFrom}}» на «{{changeTo}}»", + "ep_comments_page.comments_template.suggest_change_from": "Запропонувати зміну з «{{changeFrom}}» на", + "ep_comments_page.comments_template.to": "Кому:", + "ep_comments_page.comments_template.include_suggestion": "Включити запропоновану зміну", + "ep_comments_page.comments_template.comment.value": "Коментар", + "ep_comments_page.comments_template.cancel.value": "Скасувати", + "ep_comments_page.comments_template.reply.value": "Відповісти", + "ep_comments_page.comments_template.reply.placeholder": "Відповісти", + "ep_comments_page.comments_template.edit_comment.save": "зберегти", + "ep_comments_page.comments_template.edit_comment.cancel": "скасувати", + "ep_comments_page.error.edit_unauth": "Ви не можете редагувати коментарі інших користувачів!", + "ep_comments_page.error.delete_unauth": "Ви не можете видаляти коментарі інших користувачів!" +} diff --git a/locales/xmf.json b/locales/xmf.json new file mode 100644 index 00000000..eb5faa31 --- /dev/null +++ b/locales/xmf.json @@ -0,0 +1,17 @@ +{ + "@metadata": { + "authors": [ + "Narazeni" + ] + }, + "ep_comments_page.delete_comment.title": "ათე კომენტარიშ ლასუა", + "ep_comments_page.show_comments": "კომენტარეფიშ ძირაფა", + "ep_comments_page.comments_template.accept_change.value": "თირუაშ მეღება", + "ep_comments_page.comments_template.comment.value": "კომენტარი", + "ep_comments_page.comments_template.cancel.value": "გოუქვაფა", + "ep_comments_page.comments_template.reply.value": "გამაშ მეჭარუა", + "ep_comments_page.comments_template.edit_comment.save": "ჩუალა", + "ep_comments_page.comments_template.edit_comment.cancel": "გოუქვაფა", + "ep_comments_page.error.edit_unauth": "თქვა ვეშეილებჷნა შხვა მახვარებუეფიშ კომენტარეფიშ რედაქტირაფა!", + "ep_comments_page.error.delete_unauth": "თქვა ვეშეილებჷნა შხვა მახვარებუეფიშ კომენტარეფიშ ლასუა!" +} diff --git a/locales/zh-hans.json b/locales/zh-hans.json new file mode 100644 index 00000000..38ce699f --- /dev/null +++ b/locales/zh-hans.json @@ -0,0 +1,32 @@ +{ + "@metadata": { + "authors": [ + "TsuyaMarisa", + "列维劳德" + ] + }, + "ep_comments_page.comment": "评论", + "ep_comments_page.comments": "评论", + "ep_comments_page.add_comment.title": "在选项中添加新评论", + "ep_comments_page.add_comment": "添加新的评论", + "ep_comments_page.add_comment.hint": "请先选择文本用以评论", + "ep_comments_page.delete_comment.title": "删除此评论", + "ep_comments_page.edit_comment.title": "编辑此注释", + "ep_comments_page.show_comments": "显示评论", + "ep_comments_page.comments_template.suggested_change": "建议更改", + "ep_comments_page.comments_template.from": "来自", + "ep_comments_page.comments_template.accept_change.value": "接受更改", + "ep_comments_page.comments_template.revert_change.value": "最近更改", + "ep_comments_page.comments_template.suggested_change_from": "从“{{changeFrom}}”提出的变化到“{{changeTo}}”", + "ep_comments_page.comments_template.suggest_change_from": "建议从“{{changeFrom}}”变化", + "ep_comments_page.comments_template.to": "至", + "ep_comments_page.comments_template.include_suggestion": "包括建议的变化", + "ep_comments_page.comments_template.comment.value": "注释", + "ep_comments_page.comments_template.cancel.value": "取消", + "ep_comments_page.comments_template.reply.value": "回复", + "ep_comments_page.comments_template.reply.placeholder": "回复", + "ep_comments_page.comments_template.edit_comment.save": "保存", + "ep_comments_page.comments_template.edit_comment.cancel": "取消", + "ep_comments_page.error.edit_unauth": "您不能编辑其他用户的评论!", + "ep_comments_page.error.delete_unauth": "您不能删除其他用户的评论!" +} diff --git a/locales/zh-hant.json b/locales/zh-hant.json new file mode 100644 index 00000000..03703c7f --- /dev/null +++ b/locales/zh-hant.json @@ -0,0 +1,31 @@ +{ + "@metadata": { + "authors": [ + "Kly" + ] + }, + "ep_comments_page.comment": "意見", + "ep_comments_page.comments": "意見", + "ep_comments_page.add_comment.title": "在所選添加新的意見", + "ep_comments_page.add_comment": "在所選添加新的意見", + "ep_comments_page.add_comment.hint": "請先選擇意見內容的文字", + "ep_comments_page.delete_comment.title": "刪除此意見", + "ep_comments_page.edit_comment.title": "編輯此意見", + "ep_comments_page.show_comments": "顯示意見", + "ep_comments_page.comments_template.suggested_change": "建議更改", + "ep_comments_page.comments_template.from": "來自", + "ep_comments_page.comments_template.accept_change.value": "接受更改", + "ep_comments_page.comments_template.revert_change.value": "回復更改", + "ep_comments_page.comments_template.suggested_change_from": "將「{{changeFrom}}」改成「{{changeTo}}」的建議更改", + "ep_comments_page.comments_template.suggest_change_from": "建議更改,將「{{changeFrom}}」改成", + "ep_comments_page.comments_template.to": "至", + "ep_comments_page.comments_template.include_suggestion": "包含建議更改", + "ep_comments_page.comments_template.comment.value": "意見", + "ep_comments_page.comments_template.cancel.value": "取消", + "ep_comments_page.comments_template.reply.value": "回覆", + "ep_comments_page.comments_template.reply.placeholder": "回覆", + "ep_comments_page.comments_template.edit_comment.save": "儲存", + "ep_comments_page.comments_template.edit_comment.cancel": "取消", + "ep_comments_page.error.edit_unauth": "您不能編輯其他使用者的意見!", + "ep_comments_page.error.delete_unauth": "您不能刪除其他使用者的意見!" +} diff --git a/package-lock.json b/package-lock.json index fd416dd3..88f5ce03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,92 +1,248 @@ { "name": "ep_comments_page", - "version": "0.1.68", + "version": "1.0.3", "lockfileVersion": 1, "requires": true, "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "@eslint/eslintrc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + } + }, + "@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" } }, - "@babel/helper-validator-identifier": { - "version": "7.14.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", - "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, - "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@rushstack/eslint-patch": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz", + "integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.31.0.tgz", + "integrity": "sha512-VKW4JPHzG5yhYQrQ1AzXgVgX8ZAJEvCz0QI6mLRX4tf7rnFfh5D8SKm0Pq6w5PyNfAWJk6sv313+nEt3ohWMBQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.31.0", + "@typescript-eslint/type-utils": "5.31.0", + "@typescript-eslint/utils": "5.31.0", + "debug": "^4.3.4", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ms": "2.1.2" } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true } } }, - "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "@typescript-eslint/parser": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.31.0.tgz", + "integrity": "sha512-UStjQiZ9OFTFReTrN+iGrC6O/ko9LVDhreEK5S3edmXgR396JGq7CoX2TWIptqt/ESzU2iRKXAHfSF2WJFcWHw==", "dev": true, "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" + "@typescript-eslint/scope-manager": "5.31.0", + "@typescript-eslint/types": "5.31.0", + "@typescript-eslint/typescript-estree": "5.31.0", + "debug": "^4.3.4" + }, + "dependencies": { + "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" + } + } } }, - "@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "@typescript-eslint/scope-manager": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.31.0.tgz", + "integrity": "sha512-8jfEzBYDBG88rcXFxajdVavGxb5/XKXyvWgvD8Qix3EEJLCFIdVloJw+r9ww0wbyNLOTYyBsR+4ALNGdlalLLg==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" + "@typescript-eslint/types": "5.31.0", + "@typescript-eslint/visitor-keys": "5.31.0" } }, - "@humanwhocodes/object-schema": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", - "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "@typescript-eslint/type-utils": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.31.0.tgz", + "integrity": "sha512-7ZYqFbvEvYXFn9ax02GsPcEOmuWNg+14HIf4q+oUuLnMbpJ6eHAivCg7tZMVwzrIuzX3QCeAOqKoyMZCv5xe+w==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.31.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "dependencies": { + "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" + } + } + } + }, + "@typescript-eslint/types": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.31.0.tgz", + "integrity": "sha512-/f/rMaEseux+I4wmR6mfpM2wvtNZb1p9hAV77hWfuKc3pmaANp5dLAZSiE3/8oXTYTt3uV9KW5yZKJsMievp6g==", "dev": true }, + "@typescript-eslint/typescript-estree": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.31.0.tgz", + "integrity": "sha512-3S625TMcARX71wBc2qubHaoUwMEn+l9TCsaIzYI/ET31Xm2c9YQ+zhGgpydjorwQO9pLfR/6peTzS/0G3J/hDw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.31.0", + "@typescript-eslint/visitor-keys": "5.31.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "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" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.31.0.tgz", + "integrity": "sha512-kcVPdQS6VIpVTQ7QnGNKMFtdJdvnStkqS5LeALr4rcwx11G6OWb2HB17NMPnlRHvaZP38hL9iK8DdE9Fne7NYg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.31.0", + "@typescript-eslint/types": "5.31.0", + "@typescript-eslint/typescript-estree": "5.31.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.31.0.tgz", + "integrity": "sha512-ZK0jVxSjS4gnPirpVjXHz7mgdOsZUHzNYSfTw2yPa3agfbt9YfqaBiBZFSSxeBWnpWkzCxTfUpnzA3Vily/CSg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.31.0", + "eslint-visitor-keys": "^3.3.0" + } + }, "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "dev": true }, "acorn-jsx": { @@ -113,34 +269,56 @@ "uri-js": "^4.2.2" } }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" } }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "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 + }, + "array-includes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array.prototype.flat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" } }, "arraybuffer.slice": { @@ -149,16 +327,15 @@ "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", "dev": true }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "backo2": { @@ -200,6 +377,24 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "requires": { + "semver": "^7.0.0" + } + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -224,47 +419,6 @@ "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "cheerio": { @@ -291,18 +445,18 @@ } }, "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==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "combined-stream": { @@ -339,9 +493,9 @@ "dev": true }, "cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", "dev": true }, "cross-spawn": { @@ -386,12 +540,40 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, + "dezalgo": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "integrity": "sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==", + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -432,12 +614,6 @@ "domelementtype": "1" } }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, "engine.io-client": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.2.tgz", @@ -487,20 +663,62 @@ "has-binary2": "~1.0.2" } }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, "entities": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" }, + "es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -508,63 +726,138 @@ "dev": true }, "eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", + "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", "dev": true, "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", + "@eslint/eslintrc": "^1.2.1", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", + "glob-parent": "^6.0.1", "globals": "^13.6.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" } }, "eslint-config-etherpad": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-etherpad/-/eslint-config-etherpad-2.0.0.tgz", - "integrity": "sha512-ejBTLZiXkreSHNsdHWk/vCRkieYb6CpVZb/DH2QKbYktqRN/EFgaSISLb/8n8HZA5XvLVLbRDvDyBc/h3tIEcA==", - "dev": true + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/eslint-config-etherpad/-/eslint-config-etherpad-3.0.13.tgz", + "integrity": "sha512-Bwt1gDxThlXhY6wan1fb3Jy9kI+yFGctp7+JX6Xs+BwbOdrB4qObgnLKdcLYPKPqv9c4xTSKo3C4BdhTkg7WtQ==", + "dev": true, + "requires": { + "@rushstack/eslint-patch": "^1.1.3", + "@typescript-eslint/eslint-plugin": "^5.22.0", + "@typescript-eslint/parser": "^5.22.0", + "eslint-import-resolver-typescript": "^2.7.1", + "eslint-plugin-cypress": "^2.12.1", + "eslint-plugin-eslint-comments": "^3.2.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-mocha": "^10.0.4", + "eslint-plugin-n": "^15.2.0", + "eslint-plugin-prefer-arrow": "^1.2.3", + "eslint-plugin-promise": "^6.0.0", + "eslint-plugin-you-dont-need-lodash-underscore": "^6.12.0" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-import-resolver-typescript": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz", + "integrity": "sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "glob": "^7.2.0", + "is-glob": "^4.0.3", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "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" + } + } + } + }, + "eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } }, "eslint-plugin-cypress": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.11.3.tgz", - "integrity": "sha512-hOoAid+XNFtpvOzZSNWP5LDrQBEJwbZwjib4XJ1KcRYKjeVj0mAmPmucG4Egli4j/aruv+Ow/acacoloWWCl9Q==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.12.1.tgz", + "integrity": "sha512-c2W/uPADl5kospNDihgiLc7n87t5XhUbFDoTl6CfVkmG+kDAb5Ux10V9PoLPu9N+r7znpc+iQlcmAqT1A/89HA==", "dev": true, "requires": { "globals": "^11.12.0" @@ -579,13 +872,30 @@ } }, "eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", "dev": true, "requires": { "eslint-utils": "^2.0.0", "regexpp": "^3.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } } }, "eslint-plugin-eslint-comments": { @@ -601,64 +911,82 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true } } }, - "eslint-plugin-mocha": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-9.0.0.tgz", - "integrity": "sha512-d7knAcQj1jPCzZf3caeBIn3BnW6ikcvfz0kSqQpwPYcVGLoJV5sz0l0OJB2LR8I7dvTDbqq1oV6ylhSgzA10zg==", + "eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", "dev": true, "requires": { - "eslint-utils": "^3.0.0", - "ramda": "^0.27.1" + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" }, "dependencies": { - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { - "eslint-visitor-keys": "^2.0.0" + "ms": "2.0.0" } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true } } }, - "eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "eslint-plugin-mocha": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.1.0.tgz", + "integrity": "sha512-xLqqWUF17llsogVOC+8C6/jvQ+4IoOREbN7ZCHuOHuD6cT5cDD4h7f2LgsZuzMAiwswWE21tO7ExaknHVDrSkw==", "dev": true, "requires": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", + "eslint-utils": "^3.0.0", + "rambda": "^7.1.0" + } + }, + "eslint-plugin-n": { + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.2.4.tgz", + "integrity": "sha512-tjnVMv2fiXYMnuiIFI8QMtyUFI42SckEEWvi8h68SWGWshfqO6SSCASy24dGMGAiy7NUk6DZt90DM0iNUsmQ5w==", + "dev": true, + "requires": { + "builtins": "^5.0.1", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", "ignore": "^5.1.1", - "minimatch": "^3.0.4", + "is-core-module": "^2.9.0", + "minimatch": "^3.1.2", "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "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 - } + "semver": "^7.3.7" } }, "eslint-plugin-prefer-arrow": { @@ -668,9 +996,9 @@ "dev": true }, "eslint-plugin-promise": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz", - "integrity": "sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz", + "integrity": "sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw==", "dev": true }, "eslint-plugin-you-dont-need-lodash-underscore": { @@ -683,63 +1011,49 @@ } }, "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, "requires": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" } }, "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^2.0.0" }, "dependencies": { "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true } } }, "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", "dev": true, "requires": { - "acorn": "^7.4.0", + "acorn": "^8.7.0", "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "eslint-visitor-keys": "^3.3.0" } }, - "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 - }, "esquery": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", @@ -747,14 +1061,6 @@ "dev": true, "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } } }, "esrecurse": { @@ -764,20 +1070,12 @@ "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { @@ -792,6 +1090,30 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, "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", @@ -805,11 +1127,20 @@ "dev": true }, "fast-safe-stringify": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.8.tgz", - "integrity": "sha512-lXatBjf3WPjmWD6DpIZxkeSsCOwqI0maYMpgDlx8g4U2qi4lbjA9oH/HD2a87G+KfsUmo5WbJFmqBZlPxtptag==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", "dev": true }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -819,6 +1150,24 @@ "flat-cache": "^3.0.4" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -830,15 +1179,15 @@ } }, "flatted": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", - "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -847,9 +1196,22 @@ } }, "formidable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", - "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.0.1.tgz", + "integrity": "sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ==", + "requires": { + "dezalgo": "1.0.3", + "hexoid": "1.0.0", + "once": "1.4.0", + "qs": "6.9.3" + }, + "dependencies": { + "qs": { + "version": "6.9.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz", + "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==" + } + } }, "fs.realpath": { "version": "1.0.0", @@ -863,27 +1225,55 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" + } + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" } }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -895,23 +1285,37 @@ } }, "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" } }, "globals": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", - "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", "dev": true, "requires": { "type-fest": "^0.20.2" } }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -921,6 +1325,12 @@ "function-bind": "^1.1.1" } }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true + }, "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", @@ -937,17 +1347,40 @@ "dev": true }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==" + }, "htmlparser2": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", @@ -962,9 +1395,9 @@ } }, "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "import-fresh": { @@ -1004,36 +1437,142 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, "is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dev": true, "requires": { "has": "^1.0.3" } }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" } }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "isarray": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", @@ -1046,20 +1585,13 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "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 - }, "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, "json-schema-traverse": { @@ -1074,6 +1606,15 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, "kebab-case": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/kebab-case/-/kebab-case-1.0.1.tgz", @@ -1090,6 +1631,16 @@ "type-check": "~0.4.0" } }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, "lodash.assignin": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", @@ -1100,12 +1651,6 @@ "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -1156,12 +1701,6 @@ "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1171,42 +1710,64 @@ "yallist": "^4.0.0" } }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, "mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true }, "mime-db": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", - "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true }, "mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "requires": { - "mime-db": "1.49.0" + "mime-db": "1.52.0" } }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1228,16 +1789,44 @@ } }, "object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -1256,6 +1845,30 @@ "word-wrap": "^1.2.3" } }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1277,6 +1890,12 @@ "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==", "dev": true }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1295,18 +1914,24 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -1314,18 +1939,24 @@ "dev": true }, "qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, "requires": { "side-channel": "^1.0.4" } }, - "ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "rambda": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.1.4.tgz", + "integrity": "sha512-bPK8sSiVHIC7CqdWga8R+hRi5hfc4hK6S01lZW4KrLwSNryQoKaCOJA9GNiF20J7Nbe1vejRfR37/ASQXFL5EA==", "dev": true }, "readable-stream": { @@ -1338,26 +1969,32 @@ "util-deprecate": "^1.0.1" } }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "resolve-from": { @@ -1366,6 +2003,12 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -1375,15 +2018,24 @@ "glob": "^7.1.3" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -1415,42 +2067,11 @@ "object-inspect": "^1.9.0" } }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true }, "socket.io-client": { "version": "2.4.0", @@ -1516,21 +2137,26 @@ } } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "dev": true, "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, "string_decoder": { @@ -1542,14 +2168,20 @@ } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1557,67 +2189,50 @@ "dev": true }, "superagent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", - "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.1.5.tgz", + "integrity": "sha512-HQYyGuDRFGmZ6GNC4hq2f37KnsY9Lr0/R1marNZTgMweVDQLTLJJ6DGQ9Tj/xVVs5HEnop9EMmTbywb5P30aqw==", "dev": true, "requires": { "component-emitter": "^1.3.0", - "cookiejar": "^2.1.2", - "debug": "^4.1.1", - "fast-safe-stringify": "^2.0.7", - "form-data": "^3.0.0", - "formidable": "^1.2.2", + "cookiejar": "^2.1.3", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.0.1", "methods": "^1.1.2", - "mime": "^2.4.6", - "qs": "^6.9.4", + "mime": "^2.5.0", + "qs": "^6.10.3", "readable-stream": "^3.6.0", - "semver": "^7.3.2" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" + "semver": "^7.3.7" }, "dependencies": { - "ajv": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", - "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "ms": "2.1.2" } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true } } }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "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 + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -1630,6 +2245,42 @@ "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", "dev": true }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -1645,10 +2296,28 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, + "typescript": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", + "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", + "dev": true + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, "underscore": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", - "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==" + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", + "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==" }, "uri-js": { "version": "4.4.1", @@ -1679,6 +2348,19 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -1688,8 +2370,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "ws": { "version": "7.4.6", diff --git a/package.json b/package.json index e6d5ecf5..657ae30b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "description": "Adds comments on sidebar and link it to the text. For no-skin use ep_page_view.", "name": "ep_comments_page", - "version": "0.1.68", + "version": "1.0.3", "author": { "name": "Nicolas Lescop", "email": "limplementeur@gmail.com" @@ -23,35 +23,22 @@ ], "dependencies": { "cheerio": "^0.22.0", - "formidable": "^1.2.2", - "underscore": "^1.13.1" + "formidable": "^2.0.1", + "underscore": "^1.13.2" }, "devDependencies": { - "eslint": "^7.18.0", - "eslint-config-etherpad": "^2.0.0", - "eslint-plugin-cypress": "^2.11.3", - "eslint-plugin-eslint-comments": "^3.2.0", - "eslint-plugin-mocha": "^9.0.0", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prefer-arrow": "^1.2.3", - "eslint-plugin-promise": "^5.1.0", - "eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0", + "eslint": "^8.11.0", + "eslint-config-etherpad": "^3.0.9", "socket.io-client": "^2.3.0", - "superagent": "^6.1.0" - }, - "eslintConfig": { - "root": true, - "extends": "etherpad/plugin", - "ignorePatterns": [ - "/static/js/jquery.tmpl.min.js", - "/static/js/moment-with-locales.min.js" - ] + "superagent": "^7.1.1", + "typescript": "^4.6.2" }, "scripts": { - "lint": "eslint ." + "lint": "eslint .", + "lint:fix": "eslint --fix ." }, "engines": { - "node": ">=10.13.0" + "node": ">=12.17.0" }, "peerDependencies": { "ep_etherpad-lite": ">=1.8.6" @@ -62,6 +49,6 @@ }, "funding": { "type": "individual", - "url": "http://etherpad.org/" + "url": "https://etherpad.org/" } } diff --git a/static/css/comment.css b/static/css/comment.css index 6cd37a3a..16effd4c 100644 --- a/static/css/comment.css +++ b/static/css/comment.css @@ -207,4 +207,9 @@ input.error, textarea.error { /* OTHER */ .hidden { display: none; -} \ No newline at end of file +} + +.comment-actions-wrapper .buttonicon { + display: inline-block; + opacity: .8; +} diff --git a/static/js/commentBoxes.js b/static/js/commentBoxes.js index b0571be6..695f92b5 100644 --- a/static/js/commentBoxes.js +++ b/static/js/commentBoxes.js @@ -11,13 +11,6 @@ const getCommentsContainer = () => getPadOuter().find('#comments'); /* ***** Public methods: ***** */ -const showComment = (commentId, e) => { - const commentElm = getCommentsContainer().find(`#${commentId}`); - commentElm.show(); - - highlightComment(commentId, e); -}; - const hideComment = (commentId, hideCommentTitle) => { const commentElm = getCommentsContainer().find(`#${commentId}`); commentElm.removeClass('full-display'); @@ -122,7 +115,6 @@ const shouldNotCloseComment = (e) => { return false; }; -exports.showComment = showComment; exports.hideComment = hideComment; exports.hideAllComments = hideAllComments; exports.highlightComment = highlightComment; diff --git a/static/js/index.js b/static/js/index.js index 3fce5826..500801fa 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -60,12 +60,12 @@ const EpComments = function (context) { this.mapFakeComments = []; this.mapOriginalCommentsId = []; this.shouldCollectComment = false; - this.init(); + this.initDone = this.init(); this.preCommentMarker = preCommentMark.init(this.ace); }; // Init Etherpad plugin comment pads -EpComments.prototype.init = function () { +EpComments.prototype.init = async function () { const self = this; moment.locale(html10n.getLanguage()); @@ -76,22 +76,17 @@ EpComments.prototype.init = function () { // Init icons container commentIcons.insertContainer(); - // Get all comments - this.getComments((comments) => { - if (!$.isEmptyObject(comments)) { - this.setComments(comments); - this.collectComments(); - } - }); - - this.getCommentReplies((replies) => { - if (!$.isEmptyObject(replies)) { - this.commentReplies = replies; - this.collectCommentReplies(); - } - this.commentRepliesListen(); - this.commentListen(); - }); + const [comments, replies] = await Promise.all([this.getComments(), this.getCommentReplies()]); + if (!$.isEmptyObject(comments)) { + this.setComments(comments); + this.collectComments(); + } + if (!$.isEmptyObject(replies)) { + this.commentReplies = replies; + this.collectCommentReplies(); + } + this.commentRepliesListen(); + this.commentListen(); // Init add push event this.pushComment('add', (commentId, comment) => { @@ -135,6 +130,7 @@ EpComments.prototype.init = function () { commentId, authorId: clientVars.userId, }); + commentBoxes.hideComment(commentId); } catch (err) { if (err.message !== 'unauth') throw err; // Let the uncaught error handler handle it. $.gritter.add({ @@ -170,20 +166,46 @@ EpComments.prototype.init = function () { // Here, it adds a form to edit the comment text this.container.parent().on('click', '.comment-edit', function () { const $commentBox = $(this).closest('.comment-container'); - $commentBox.addClass('editing'); + const changeAcceptedState = $commentBox.hasClass('change-accepted'); + $commentBox.addClass('editing'); + const changeTo = $commentBox.find('.to-value').first().text(); + const changeFrom = $commentBox.find('.from-value').first().text(); const textBox = self.findCommentText($commentBox).last(); + const commentId = $commentBox.data('commentid'); // if edit form not already there if (textBox.siblings('.comment-edit-form').length === 0) { // add a form to edit the field const data = {}; + data.changeAcceptedState = changeAcceptedState; + data.commentId = commentId; data.text = textBox.text(); + data.changeFrom = changeFrom; + data.changeTo = changeTo; const content = $('#editCommentTemplate').tmpl(data); // localize the comment/reply edit form commentL10n.localize(content); // insert form textBox.before(content); + const editForm = textBox.parent().find('.comment-edit-form').first(); + if (changeTo) { + editForm.find('.suggestion').show(); + editForm.find('.label-suggestion-checkbox') + .siblings('input[type="checkbox"]') + .prop('checked', true); + } + editForm.on('change', '.suggestion-checkbox', function () { + if ($(this).is(':checked')) { + editForm.find('.suggestion').show(); + } else { + editForm.find('.suggestion').hide(); + } + }); + + editForm.find('.label-suggestion-checkbox').click(function () { + $(this).siblings('input[type="checkbox"]').click(); + }); } }); @@ -195,14 +217,25 @@ EpComments.prototype.init = function () { const $commentForm = $(this).closest('.comment-edit-form'); const commentId = $commentBox.data('commentid'); const commentText = $commentForm.find('.comment-edit-text').val(); + const changeFrom = $commentForm.find('.from-value').first().text(); + const changeTo = $commentForm.find('.to-value').val(); + const data = {}; data.commentId = commentId; data.padId = clientVars.padId; data.commentText = commentText; data.authorId = clientVars.userId; + data.changeFrom = null; + data.changeTo = null; + if ($commentForm.find('.label-suggestion-checkbox') + .siblings('input[type="checkbox"]') + .is(':checked')) { + data.changeFrom = changeFrom; + data.changeTo = changeTo; + } try { - await self._send('updateCommentText', data); + await self._send('updateComment', data); } catch (err) { if (err.message !== 'unauth') throw err; // Let the uncaught error handler handle it. $.gritter.add({ @@ -216,7 +249,7 @@ EpComments.prototype.init = function () { $commentForm.remove(); $commentBox.removeClass('editing'); self.updateCommentBoxText(commentId, commentText); - + self.updateCommentBoxChangeTo(commentId, data); // although the comment or reply was saved on the data base successfully, it needs // to update the comment or comment reply variable with the new text saved self.setCommentOrReplyNewText(commentId, commentText); @@ -304,7 +337,7 @@ EpComments.prototype.init = function () { }); // When a reply get submitted - this.container.parent().on('submit', '.new-comment', function (e) { + this.container.parent().on('submit', '.new-comment', async function (e) { e.preventDefault(); const data = self.getCommentData(); @@ -312,17 +345,13 @@ EpComments.prototype.init = function () { data.reply = $(this).find('.comment-content').val(); data.changeTo = $(this).find('.to-value').val() || null; data.changeFrom = $(this).find('.from-value').text() || null; - self._send('addCommentReply', data).then(() => { - self.getCommentReplies((replies) => { - self.commentReplies = replies; - self.collectCommentReplies(); - - // Once the new reply is displayed, we clear the form - $('iframe[name="ace_outer"]').contents().find('.new-comment').removeClass('editing'); - }); - }); - $(this).trigger('reset_reply'); + await self._send('addCommentReply', data); + const replies = await self.getCommentReplies(); + self.commentReplies = replies; + self.collectCommentReplies(); + // Once the new reply is displayed, we clear the form + $('iframe[name="ace_outer"]').contents().find('.new-comment').removeClass('editing'); }); this.container.parent().on('reset_reply', '.new-comment', function (e) { // Reset the form @@ -389,6 +418,7 @@ EpComments.prototype.findCommentText = function ($commentBox) { return $commentBox.find('.compact-display-content .comment-text, ' + '.full-display-content .comment-title-wrapper .comment-text'); }; + // This function is useful to collect new comments on the collaborators EpComments.prototype.collectCommentsAfterSomeIntervalsOfTime = async function () { await new Promise((resolve) => window.setTimeout(resolve, 300)); @@ -599,6 +629,10 @@ EpComments.prototype.insertComment = function (commentId, comment, index) { comment.commentId = commentId; comment.reply = true; content = $('#commentsTemplate').tmpl(comment); + content.find('.from-label')[0].dataset.l10nArgs = JSON.stringify({ + changeFrom: comment.changeFrom, + changeTo: comment.changeTo, + }); if (comment.author !== clientVars.userId) { $(content).find('.comment-actions-wrapper').addClass('hidden'); } @@ -711,12 +745,14 @@ EpComments.prototype.localizeExistingComments = function () { const commentElm = self.container.find(`#${commentId}`); const comment = comments[commentId]; - // localize comment element... - commentL10n.localize(commentElm); - // ... and update its date - comment.data.date = moment(comment.data.timestamp).fromNow(); - comment.data.formattedDate = new Date(comment.data.timestamp).toISOString(); - $(commentElm).find('.comment-created-at').html(comment.data.date); + if (comment) { // https://github.com/ether/ep_comments_page/issues/197 + // localize comment element... + commentL10n.localize(commentElm); + // ... and update its date + comment.data.date = moment(comment.data.timestamp).fromNow(); + comment.data.formattedDate = new Date(comment.data.timestamp).toISOString(); + $(commentElm).find('.comment-created-at').html(comment.data.date); + } } }); }; @@ -765,13 +801,13 @@ EpComments.prototype._send = async function (type, ...args) { }; // Get all comments -EpComments.prototype.getComments = function (callback) { - this._send('getComments', {padId: this.padId}).then((res) => callback(res.comments)); +EpComments.prototype.getComments = async function () { + return (await this._send('getComments', {padId: this.padId})).comments; }; // Get all comment replies -EpComments.prototype.getCommentReplies = function (callback) { - this._send('getCommentReplies', {padId: this.padId}).then((res) => callback(res.replies)); +EpComments.prototype.getCommentReplies = async function () { + return (await this._send('getCommentReplies', {padId: this.padId})).replies; }; EpComments.prototype.getCommentData = function () { @@ -794,7 +830,7 @@ EpComments.prototype.getCommentData = function () { // Delete a pad comment EpComments.prototype.deleteComment = function (commentId) { - $('iframe[name="ace_outer"]').contents().find(`#${commentId}`).remove(); + $('iframe[name="ace_outer"]').contents().find(`#icon-${commentId}`).remove(); }; const cloneLine = (line) => { @@ -906,12 +942,6 @@ EpComments.prototype.displayNewCommentForm = function () { // Write the text to the changeFrom form $('#newComment').find('.from-value').text(selectedText); - // Display form - setTimeout(() => { - const position = getXYOffsetOfRep(rep); - newComment.showNewCommentPopup(position); - }); - // Check if the first element selected is visible in the viewport const $firstSelectedElement = this.getFirstElementSelected(); const firstSelectedElementInViewport = this.isElementInViewport($firstSelectedElement); @@ -920,8 +950,9 @@ EpComments.prototype.displayNewCommentForm = function () { this.scrollViewportIfSelectedTextIsNotVisible($firstSelectedElement); } - // Adjust focus on the form - $('#newComment').find('.comment-content').focus(); + // Display form + const position = getXYOffsetOfRep(rep); + newComment.showNewCommentPopup(position); }; EpComments.prototype.scrollViewportIfSelectedTextIsNotVisible = function ($firstSelectedElement) { @@ -1054,33 +1085,31 @@ EpComments.prototype.lineHasMarker = function (line) { }; // Save comment -EpComments.prototype.saveComment = function (data, rep) { - this._send('addComment', data).then((res) => { - if (res == null) return; - const [commentId, comment] = res; - comment.commentId = commentId; - - this.ace.callWithAce((ace) => { - // we should get rep again because the document might have changed.. - // details at https://github.com/ether/ep_comments/issues/133 - rep = ace.ace_getRep(); - ace.ace_performSelectionChange(rep.selStart, rep.selEnd, true); - ace.ace_setAttributeOnSelection('comment', commentId); - }, 'insertComment', true); +EpComments.prototype.saveComment = async function (data, rep) { + const res = await this._send('addComment', data); + if (res == null) return; + const [commentId, comment] = res; + comment.commentId = commentId; - this.setComment(commentId, comment); - this.collectComments(); - }); + this.ace.callWithAce((ace) => { + // we should get rep again because the document might have changed.. + // details at https://github.com/ether/ep_comments/issues/133 + rep = ace.ace_getRep(); + ace.ace_performSelectionChange(rep.selStart, rep.selEnd, true); + ace.ace_setAttributeOnSelection('comment', commentId); + }, 'insertComment', true); + + this.setComment(commentId, comment); + this.collectComments(); }; // commentData = {c-newCommentId123: data:{author:..., date:..., ...}, // c-newCommentId124: data:{...}} -EpComments.prototype.saveCommentWithoutSelection = function (padId, commentData) { +EpComments.prototype.saveCommentWithoutSelection = async function (padId, commentData) { const data = this.buildComments(commentData); - this._send('bulkAddComment', padId, data).then((comments) => { - this.setComments(comments); - this.shouldCollectComment = true; - }); + const comments = await this._send('bulkAddComment', padId, data); + this.setComments(comments); + this.shouldCollectComment = true; }; EpComments.prototype.buildComments = function (commentsData) { @@ -1108,14 +1137,13 @@ EpComments.prototype.getMapfakeComments = function () { }; // commentReplyData = {c-reply-123:{commentReplyData1}, c-reply-234:{commentReplyData1}, ...} -EpComments.prototype.saveCommentReplies = function (padId, commentReplyData) { +EpComments.prototype.saveCommentReplies = async function (padId, commentReplyData) { const data = this.buildCommentReplies(commentReplyData); - this._send('bulkAddCommentReplies', padId, data).then((replies) => { - _.each(replies, (reply) => { - this.setCommentReply(reply); - }); - this.shouldCollectComment = true; // force collect the comment replies saved + const replies = await this._send('bulkAddCommentReplies', padId, data); + _.each(replies, (reply) => { + this.setCommentReply(reply); }); + this.shouldCollectComment = true; // force collect the comment replies saved }; EpComments.prototype.buildCommentReplies = function (repliesData) { @@ -1141,33 +1169,31 @@ EpComments.prototype.buildCommentReply = function (replyData) { // Listen for comment EpComments.prototype.commentListen = function () { const socket = this.socket; - socket.on('pushAddCommentInBulk', () => { - this.getComments((allComments) => { - if (!$.isEmptyObject(allComments)) { - // we get the comments in this format {c-123:{author:...}, c-124:{author:...}} - // but it's expected to be {c-123: {data: {author:...}}, c-124:{data:{author:...}}} - // in this.comments - const commentsProcessed = {}; - _.map(allComments, (comment, commentId) => { - commentsProcessed[commentId] = {}; - commentsProcessed[commentId].data = comment; - }); - this.comments = commentsProcessed; - this.collectCommentsAfterSomeIntervalsOfTime(); // here we collect on the collaborators - } - }); + socket.on('pushAddCommentInBulk', async () => { + const allComments = await this.getComments(); + if (!$.isEmptyObject(allComments)) { + // we get the comments in this format {c-123:{author:...}, c-124:{author:...}} + // but it's expected to be {c-123: {data: {author:...}}, c-124:{data:{author:...}}} + // in this.comments + const commentsProcessed = {}; + _.map(allComments, (comment, commentId) => { + commentsProcessed[commentId] = {}; + commentsProcessed[commentId].data = comment; + }); + this.comments = commentsProcessed; + this.collectCommentsAfterSomeIntervalsOfTime(); // here we collect on the collaborators + } }); }; // Listen for comment replies EpComments.prototype.commentRepliesListen = function () { - this.socket.on('pushAddCommentReply', (replyId, reply) => { - this.getCommentReplies((replies) => { - if (!$.isEmptyObject(replies)) { - this.commentReplies = replies; - this.collectCommentReplies(); - } - }); + this.socket.on('pushAddCommentReply', async (replyId, reply) => { + const replies = await this.getCommentReplies(); + if (!$.isEmptyObject(replies)) { + this.commentReplies = replies; + this.collectCommentReplies(); + } }); }; @@ -1177,6 +1203,33 @@ EpComments.prototype.updateCommentBoxText = function (commentId, commentText) { textBox.text(commentText); }; +EpComments.prototype.updateCommentBoxChangeTo = function (commentId, data) { + const $comment = this.container.parent().find(`[data-commentid='${commentId}']`); + const $suggest = $('#display-suggestion').tmpl(data); + if (data.changeTo) { + $($suggest[2]).find('.from-label').each((key, item) => { + if (item.dataset) { + item.dataset.l10nArgs = JSON.stringify({ + changeFrom: data.changeFrom, + changeTo: data.changeTo, + }); + } + }); + + commentL10n.localize($($suggest[2])); + if (!$comment.find('.comment-changeTo-form').length) { + const textBox = this.findCommentText($comment); + textBox.after($suggest); + } + $comment.find('.comment-changeTo-form').replaceWith($suggest); + + + this.container.parent().find(`[data-commentid='${commentId}']`).replaceWith($comment); + } else { + $comment.find('.comment-changeTo-form').remove(); + } +}; + EpComments.prototype.showChangeAsAccepted = function (commentId) { const self = this; @@ -1209,6 +1262,12 @@ EpComments.prototype.pushComment = function (eventType, callback) { this.updateCommentBoxText(commentId, commentText); }); + socket.on('commentUpdated', (commentId, commentText, changeFrom, changeTo) => { + console.log('commentUpdated', changeFrom, changeTo); + this.updateCommentBoxText(commentId, commentText); + this.updateCommentBoxChangeTo(commentId, {commentId, commentText, changeFrom, changeTo}); + }); + socket.on('commentDeleted', (commentId) => { this.deleteComment(commentId); }); @@ -1240,9 +1299,10 @@ EpComments.prototype.pushComment = function (eventType, callback) { const hooks = { // Init pad comments - postAceInit: (hookName, context, cb) => { + postAceInit: async (hookName, context) => { if (!pad.plugins) pad.plugins = {}; const Comments = new EpComments(context); + await Comments.initDone; pad.plugins.ep_comments_page = Comments; if (!$('#editorcontainerbox').hasClass('flex-layout')) { @@ -1254,7 +1314,6 @@ const hooks = { class_name: 'error', }); } - return cb(); }, postToolbarInit: (hookName, args, cb) => { diff --git a/static/js/newComment.js b/static/js/newComment.js index 1eb6e658..4e4fdcee 100644 --- a/static/js/newComment.js +++ b/static/js/newComment.js @@ -2,6 +2,8 @@ const commentL10n = require('ep_comments_page/static/js/commentL10n'); +let $newComment = $(); + // Create a comment object with data filled on the given form const buildCommentFrom = (form) => { const text = form.find('.comment-content').val(); @@ -26,15 +28,16 @@ const cancelNewComment = () => { // Callback for new comment Submit const submitNewComment = (callback) => { const index = 0; - const form = $('#newComment'); - const comment = buildCommentFrom(form); + const comment = buildCommentFrom($newComment); if (comment.text.length > 0 || comment.changeTo && comment.changeTo.length > 0) { - form.find('.comment-content, .to-value').removeClass('error'); + $newComment.find('.comment-content, .to-value').removeClass('error'); hideNewCommentPopup(); callback(comment, index); } else { - if (comment.text.length === 0) form.find('.comment-content').addClass('error'); - if (comment.changeTo && comment.changeTo.length === 0) form.find('.to-value').addClass('error'); + if (comment.text.length === 0) $newComment.find('.comment-content').addClass('error'); + if (comment.changeTo && comment.changeTo.length === 0) { + $newComment.find('.to-value').addClass('error'); + } } return false; }; @@ -42,36 +45,35 @@ const submitNewComment = (callback) => { /* ***** Public methods: ***** */ const localizenewCommentPopup = () => { - const newCommentPopup = $('#newComment'); - if (newCommentPopup.length !== 0) commentL10n.localize(newCommentPopup); + if ($newComment.length !== 0) commentL10n.localize($newComment); }; // Insert new Comment Form const insertNewCommentPopupIfDontExist = (comment, callback) => { - $('#newComment').remove(); + $newComment.remove(); comment.commentId = ''; - const newCommentPopup = $('#newCommentTemplate').tmpl(comment); - newCommentPopup.appendTo($('#editorcontainerbox')); + $newComment = $('#newCommentTemplate').tmpl(comment); + $newComment.appendTo($('#editorcontainerbox')); localizenewCommentPopup(); // Listen for include suggested change toggle - $('#newComment').find('.suggestion-checkbox').change(function () { - $('#newComment').find('.suggestion').toggle($(this).is(':checked')); + $newComment.find('.suggestion-checkbox').change(function () { + $newComment.find('.suggestion').toggle($(this).is(':checked')); }); // Cancel btn - newCommentPopup.find('#comment-reset').on('click', () => { + $newComment.find('#comment-reset').on('click', () => { cancelNewComment(); }); // Create btn - $('#newComment').on('submit', (e) => { + $newComment.on('submit', (e) => { e.preventDefault(); return submitNewComment(callback); }); - return newCommentPopup; + return $newComment; }; const showNewCommentPopup = (position) => { @@ -82,27 +84,29 @@ const showNewCommentPopup = (position) => { left = $('.toolbar .addComment').offset().left; } const top = position[1]; - $('#newComment').css('left', left); + $newComment.css('left', left); if (left === position[0]) { - $('#newComment').css('top', top); + $newComment.css('top', top); } // Reset form to make sure it is all clear - $('#newComment').find('.suggestion-checkbox').prop('checked', false).trigger('change'); - $('#newComment').find('textarea').val(''); - $('#newComment').find('.comment-content, .to-value').removeClass('error'); + $newComment.find('.suggestion-checkbox').prop('checked', false).trigger('change'); + $newComment.find('textarea').val(''); + $newComment.find('.comment-content, .to-value').removeClass('error'); // Show popup - $('#newComment').addClass('popup-show'); - // mark selected text, so it is clear to user which text range the comment is being applied to pad.plugins.ep_comments_page.preCommentMarker.markSelectedText(); + setTimeout(() => { + $newComment.addClass('popup-show'); + $newComment.find('.comment-content').focus(); + }); }; const hideNewCommentPopup = () => { - $('#newComment').removeClass('popup-show'); + $newComment.removeClass('popup-show'); // force focus to be lost, so virtual keyboard is hidden on mobile devices - $('#newComment').find(':focus').blur(); + $newComment.find(':focus').blur(); // unmark selected text, as now there is no text being commented pad.plugins.ep_comments_page.preCommentMarker.unmarkSelectedText(); diff --git a/static/tests/backend/specs/api/exportHTML.js b/static/tests/backend/specs/api/exportHTML.js index ddaee6cb..484f005e 100644 --- a/static/tests/backend/specs/api/exportHTML.js +++ b/static/tests/backend/specs/api/exportHTML.js @@ -140,7 +140,7 @@ describe(__filename, function () { context('when pad text has comment in strong', function () { before(async function () { html = () => buildHTML( - textWithComment('c-2342', 'this is a comment and bold') + textWithComment('c-2342', 'this is a comment and bold'), ); }); diff --git a/static/tests/backend/specs/readOnlyPad.js b/static/tests/backend/specs/readOnlyPad.js new file mode 100644 index 00000000..127c471a --- /dev/null +++ b/static/tests/backend/specs/readOnlyPad.js @@ -0,0 +1,134 @@ +'use strict'; + +const AttributePool = require('ep_etherpad-lite/static/js/AttributePool'); +const Changeset = require('ep_etherpad-lite/static/js/Changeset'); +const assert = require('assert').strict; +const common = require('ep_etherpad-lite/tests/backend/common'); +const padManager = require('ep_etherpad-lite/node/db/PadManager'); +const readOnlyManager = require('ep_etherpad-lite/node/db/ReadOnlyManager'); +const shared = require('../../../js/shared.js'); + +describe(__filename, function () { + let agent; + let pad; + let padId; + let roPadId; + let socket; + + const makeUserChanges = (opcode, attribs) => { + const oldLen = pad.text().length; + assert(oldLen > 0); + const apool = new AttributePool(); + const op = Changeset.newOp(opcode); + op.chars = 1; + op.attribs = Changeset.makeAttribsString(opcode, attribs, apool); + const assem = Changeset.smartOpAssembler(); + assem.append(op); + const cs = assem.toString(); + const newLen = oldLen + assem.getLengthChange(); + const changeset = Changeset.pack(oldLen, newLen, cs, opcode === '+' ? 'x' : ''); + return {baseRev: pad.head, changeset, apool: apool.toJsonable()}; + }; + + before(async function () { + agent = await common.init(); + }); + + beforeEach(async function () { + padId = `testpad${common.randomString()}`; + assert(!await padManager.doesPadExist(padId)); + pad = await padManager.getPad(padId, 'text'); + assert(pad.text().startsWith('text')); + roPadId = await readOnlyManager.getReadOnlyId(padId); + const res = await agent.get(`/p/${roPadId}`).expect(200); + socket = await common.connect(res); + const {type, data: clientVars} = await common.handshake(socket, roPadId); + assert.equal(type, 'CLIENT_VARS'); + assert(clientVars.readonly); + assert.equal(clientVars.readOnlyId, roPadId); + }); + + afterEach(async function () { + if (socket != null) socket.close(); + socket = null; + if (pad != null) await pad.remove(); + pad = null; + }); + + describe('comment-only changes are accepted', function () { + it('add/change comment attribute', async function () { + await Promise.all([ + common.waitForAcceptCommit(socket, pad.head + 1), + common.sendUserChanges( + socket, makeUserChanges('=', [['comment', shared.generateCommentId()]])), + ]); + }); + + it('remove comment attribute', async function () { + await Promise.all([ + common.waitForAcceptCommit(socket, pad.head + 1), + common.sendUserChanges( + socket, makeUserChanges('=', [['comment', shared.generateCommentId()]])), + ]); + await Promise.all([ + common.waitForAcceptCommit(socket, pad.head + 1), + common.sendUserChanges(socket, makeUserChanges('=', [['comment', '']])), + ]); + }); + }); + + describe('other changes are rejected', function () { + const testCases = [ + { + desc: 'keep with non-comment attrib add/change', + opcode: '=', + attribs: [['bold', 'true']], + }, + { + desc: 'keep with non-comment attrib removal', + opcode: '=', + attribs: [['bold', '']], + }, + { + desc: 'keep with comment and non-comment attrib adds/changes', + opcode: '=', + attribs: [['comment', shared.generateCommentId()], ['bold', 'true']], + }, + { + desc: 'insert with no attribs', + opcode: '+', + attribs: [], + }, + { + desc: 'insert with comment attrib', + opcode: '+', + attribs: [['comment', shared.generateCommentId()]], + }, + { + desc: 'insert with non-comment attrib', + opcode: '+', + attribs: [['bold', 'true']], + }, + { + desc: 'insert with comment and non-comment attribs', + opcode: '+', + attribs: [['comment', shared.generateCommentId()], ['bold', 'true']], + }, + { + desc: 'remove', + opcode: '-', + attribs: [], + }, + ]; + + for (const {desc, opcode, attribs} of testCases) { + it(desc, async function () { + const head = pad.head; + await assert.rejects(common.sendUserChanges(socket, makeUserChanges(opcode, attribs))); + // common.sendUserChanges() waits for message ack, so if the message was accepted then head + // should have already incremented by the time we get here. + assert.equal(pad.head, head); + }); + } + }); +}); diff --git a/static/tests/frontend/specs/commentDelete.js b/static/tests/frontend/specs/commentDelete.js index 15d0ecce..ce5de371 100644 --- a/static/tests/frontend/specs/commentDelete.js +++ b/static/tests/frontend/specs/commentDelete.js @@ -1,110 +1,87 @@ 'use strict'; -describe('ep_comments_page - Comment Delete', function () { - let helperFunctions; - const textOfComment = 'original comment'; - const textOfReply = 'original reply'; - const FIRST_LINE = 0; +const utils = require('../utils'); - // create pad with a comment and a reply - beforeEach(function (done) { - helperFunctions = commentDelete; - helperFunctions.createPad(this, () => { - helperFunctions.addComentAndReplyToLine(FIRST_LINE, textOfComment, textOfReply, done); - }); - }); +let helperFunctions; +const textOfComment = 'original comment'; +const textOfReply = 'original reply'; +const FIRST_LINE = 0; - context('when user presses the delete button on a comment', function () { - it('should delete comment', function (done) { - const outer$ = helper.padOuter$; - const inner$ = helper.padInner$; - outer$('.comment-delete').click(); - helper.waitFor(() => inner$('.comment').length === 0).done(() => { - if (inner$('.comment').length !== 0) throw new Error('Error deleting comment'); - done(); - }); +// create pad with a comment and a reply +beforeEach(async function () { + helperFunctions = commentDelete; + await helperFunctions.createPad(this); + await helperFunctions.addCommentAndReplyToLine(FIRST_LINE, textOfComment, textOfReply); +}); + +context('when user presses the delete button on a comment', function () { + it('should delete comment', function (done) { + const outer$ = helper.padOuter$; + const inner$ = helper.padInner$; + outer$('.comment-delete').click(); + helper.waitFor(() => inner$('.comment').length === 0).done(() => { + if (inner$('.comment').length !== 0) throw new Error('Error deleting comment'); + done(); }); }); +}); - context('when user presses the delete button on other users comment', function () { - it('should not delete comment', async function () { - let outer$ = helper.padOuter$; - await new Promise((resolve) => setTimeout(resolve, 500)); - await new Promise((resolve) => helper.newPad(resolve, helperFunctions.padId)); - await helper.waitForPromise(() => { - outer$ = helper.padOuter$; - return !!outer$ && outer$('.comment-delete').length; - }); - outer$('.comment-delete').click(); - await helper.waitForPromise(() => { - const chrome$ = helper.padChrome$; - return chrome$('#gritter-container').find('.error').length > 0; - }); - const inner$ = helper.padInner$; - if (inner$('.comment').length === 0) throw new Error('Comment should not have been deleted'); +context('when user presses the delete button on other users comment', function () { + it('should not delete comment', async function () { + let outer$ = helper.padOuter$; + await new Promise((resolve) => setTimeout(resolve, 500)); + await utils.aNewPad({id: helperFunctions.padId}); + await helper.waitForPromise(() => { + outer$ = helper.padOuter$; + return !!outer$ && outer$('.comment-delete').length; + }); + outer$('.comment-delete').click(); + await helper.waitForPromise(() => { + const chrome$ = helper.padChrome$; + return chrome$('#gritter-container').find('.error').length > 0; }); + const inner$ = helper.padInner$; + if (inner$('.comment').length === 0) throw new Error('Comment should not have been deleted'); }); }); const commentDelete = { padId: undefined, - createPad(test, cb) { - const self = this; - this.padId = helper.newPad(() => { - self.enlargeScreen(() => { - self.createOrResetPadText(() => { - cb(); - }); - }); - }); + async createPad(test) { test.timeout(60000); + this.padId = await utils.aNewPad(); + this.enlargeScreen(); + await this.createOrResetPadText(); }, - createOrResetPadText(cb) { - this.cleanPad(() => { + async createOrResetPadText() { + await this.cleanPad(); + const inner$ = helper.padInner$; + inner$('div').first().sendkeys('something\n anything'); + await helper.waitForPromise(() => { const inner$ = helper.padInner$; - inner$('div').first().sendkeys('something\n anything'); - helper.waitFor(() => { - const inner$ = helper.padInner$; - const lineLength = inner$('div').length; - - return lineLength > 1; - }).done(cb); + const lineLength = inner$('div').length; + return lineLength > 1; }); }, - reloadPad(test, cb) { - test.timeout(20000); - const self = this; - const padId = this.padId; - // we do nothing for a second while we wait for content to be collected before reloading - // this may be hacky, but we need time for CC to run so... :? - setTimeout(() => { - helper.newPad(() => { - self.enlargeScreen(cb); - }, padId); - }, 1000); - }, - cleanPad(callback) { + async cleanPad() { const inner$ = helper.padInner$; const $padContent = inner$('#innerdocbody'); $padContent.html(' '); // wait for Etherpad to re-create first line - helper.waitFor(() => { + await helper.waitForPromise(() => { const lineNumber = inner$('div').length; return lineNumber === 1; - }, 20000).done(callback); + }, 20000); }, - enlargeScreen(callback) { + enlargeScreen() { $('#iframe-container iframe').css('max-width', '3000px'); - callback(); }, - addComentAndReplyToLine(line, textOfComment, textOfReply, callback) { - const self = this; - this.addCommentToLine(line, textOfComment, () => { - self.addCommentReplyToLine(line, textOfReply, callback); - }); + async addCommentAndReplyToLine(line, textOfComment, textOfReply) { + await this.addCommentToLine(line, textOfComment); + await this.addCommentReplyToLine(line, textOfReply); }, - addCommentToLine(line, textOfComment, callback) { + async addCommentToLine(line, textOfComment) { const chrome$ = helper.padChrome$; const $line = this.getLine(line); $line.sendkeys('{selectall}'); // needs to select content to add comment to @@ -118,9 +95,9 @@ const commentDelete = { $submittButton.click(); // wait until comment is created and comment id is set - this.createdCommentOnLine(line, callback); + await this.createdCommentOnLine(line); }, - addCommentReplyToLine(line, textOfReply, callback) { + async addCommentReplyToLine(line, textOfReply) { const outer$ = helper.padOuter$; const commentId = this.getCommentIdOfLine(line); const existingReplies = outer$('.sidebar-comment-reply').length; @@ -141,19 +118,18 @@ const commentDelete = { $submitReplyButton.click(); // wait for the reply to be saved - helper.waitFor(() => { + await helper.waitForPromise(() => { const hasSavedReply = outer$('.sidebar-comment-reply').length === existingReplies + 1; return hasSavedReply; - }).done(callback); + }); }, getLine(lineNum) { const inner$ = helper.padInner$; const $line = inner$('div').slice(lineNum, lineNum + 1); return $line; }, - createdCommentOnLine(line, cb) { - const self = this; - helper.waitFor(() => self.getCommentIdOfLine(line) != null).done(cb); + async createdCommentOnLine(line) { + await helper.waitForPromise(() => this.getCommentIdOfLine(line) != null); }, getCommentIdOfLine(line) { const $line = this.getLine(line); @@ -167,40 +143,4 @@ const commentDelete = { commentIconsEnabled() { return helper.padOuter$('#commentIcons').length > 0; }, - clickEditCommentButton() { - const outer$ = helper.padOuter$; - const $editButton = outer$('.comment-edit').first(); - $editButton.click(); - }, - clickEditCommentReplyButton() { - const outer$ = helper.padOuter$; - const $editButton = outer$('.comment-edit').last(); - $editButton.click(); - }, - getEditForm() { - const outer$ = helper.padOuter$; - return outer$('.comment-edit-form'); - }, - checkIfOneFormEditWasAdded() { - expect(this.getEditForm().length).to.be(1); - }, - checkIfOneFormEditWasRemoved() { - expect(this.getEditForm().length).to.be(0); - }, - checkIfCommentFieldIsHidden(fieldClass) { - const outer$ = helper.padOuter$; - const $field = outer$(`.${fieldClass}`).first(); - expect($field.is(':visible')).to.be(false); - }, - pressCancel() { - const $cancelButton = this.getEditForm().find('.comment-edit-cancel'); - $cancelButton.click(); - }, - pressSave() { - const $saveButton = this.getEditForm().find('.comment-edit-submit'); - $saveButton.click(); - }, - writeCommentText(commentText) { - this.getEditForm().find('.comment-edit-text').text(commentText); - }, }; diff --git a/static/tests/frontend/specs/commentIcons.js b/static/tests/frontend/specs/commentIcons.js index 552ce2ae..3d0d6893 100644 --- a/static/tests/frontend/specs/commentIcons.js +++ b/static/tests/frontend/specs/commentIcons.js @@ -1,334 +1,298 @@ 'use strict'; -describe('ep_comments_page - Comment icons', function () { - // create a new pad with comment before each test run - beforeEach(async function () { - await new Promise((resolve) => helper.newPad(resolve)); - // make sure Etherpad has enough space to display comment icons - enlargeScreen(); - // force sidebar comments to be shown - chooseToShowComments(true); - await createComment(); - this.timeout(60000); - }); +let freshPad = true; - after(async function () { - // undo frame resize that was done on before() - $('#iframe-container iframe').css('max-width', ''); - }); +before(async function () { + await helper.aNewPad(); + // #commentIcons will only be inserted if icons are enabled + if (!helper.padChrome$.window.clientVars.displayCommentAsIcon) this.skip(); +}); - it('adds a comment icon on the same height of commented text', async function () { - // we only run test if icons are enabled - await finishTestIfIconsAreNotEnabled(async () => { - const inner$ = helper.padInner$; - const outer$ = helper.padOuter$; - const commentId = await getCommentId(); - const $commentIcon = outer$(`#commentIcons #icon-${commentId}`); - - // check icon exists - expect($commentIcon.length).to.be(1); - - // check height is the same - const $commentedText = inner$(`.${commentId}`); - // all icons are +5px down to adjust position - const expectedTop = $commentedText.offset().top + 5; - expect($commentIcon.offset().top).to.be(expectedTop); - }); - }); - // TODO: Needs fixing - xit('does not show comment icon when commented text is removed', async function () { - // we only run test if icons are enabled - await finishTestIfIconsAreNotEnabled(async () => { - const inner$ = helper.padInner$; - const outer$ = helper.padOuter$; - // remove commented text - const $commentedLine = inner$('div .comment').parent(); - $commentedLine.sendkeys('{selectall}'); // select all - $commentedLine.sendkeys('{del}'); // clear the first line - // wait until comment deletion is done - await helper.waitForPromise(() => { - // check icon is not visible - const $commentIcons = outer$('#commentIcons .comment-icon:visible'); - return $commentIcons.length === 0; - }); - }); - }); - // TODO: Needs fixing - xit('does not show comment icon when comment is deleted', async function () { - // we only run test if icons are enabled - await finishTestIfIconsAreNotEnabled(async () => { - const outer$ = helper.padOuter$; - - await deleteComment(); - // check icon is not visible - const $commentIcons = outer$('#commentIcons .comment-icon:visible'); - expect($commentIcons.length).to.be(0); - }); +// create a new pad with comment before each test run +beforeEach(async function () { + this.timeout(60000); + if (!freshPad) await helper.aNewPad(); + freshPad = false; + // make sure Etherpad has enough space to display comment icons + enlargeScreen(); + // force sidebar comments to be shown + chooseToShowComments(true); + await createComment(); +}); + +after(async function () { + // undo frame resize that was done on before() + $('#iframe-container iframe').css('max-width', ''); +}); + +it('adds a comment icon on the same height of commented text', async function () { + const inner$ = helper.padInner$; + const outer$ = helper.padOuter$; + const commentId = await getCommentId(); + const $commentIcon = outer$(`#commentIcons #icon-${commentId}`); + + // check icon exists + expect($commentIcon.length).to.be(1); + + // check height is the same + const $commentedText = inner$(`.${commentId}`); + // all icons are +5px down to adjust position + const expectedTop = $commentedText.offset().top + 5; + expect($commentIcon.offset().top).to.be(expectedTop); +}); +// TODO: Needs fixing +xit('does not show comment icon when commented text is removed', async function () { + const inner$ = helper.padInner$; + const outer$ = helper.padOuter$; + // remove commented text + const $commentedLine = inner$('div .comment').parent(); + $commentedLine.sendkeys('{selectall}'); // select all + $commentedLine.sendkeys('{del}'); // clear the first line + // wait until comment deletion is done + await helper.waitForPromise(() => { + // check icon is not visible + const $commentIcons = outer$('#commentIcons .comment-icon:visible'); + return $commentIcons.length === 0; }); +}); +// TODO: Needs fixing +xit('does not show comment icon when comment is deleted', async function () { + const outer$ = helper.padOuter$; + + await deleteComment(); + // check icon is not visible + const $commentIcons = outer$('#commentIcons .comment-icon:visible'); + expect($commentIcons.length).to.be(0); +}); - it('updates comment icon height when commented text is moved to another line', async function () { - // we only run test if icons are enabled - // don't run this test in safari. borrowed from - // https://stackoverflow.com/questions/7944460/detect-safari-browser - const ua = navigator.userAgent.toLowerCase(); - if (ua.indexOf('safari') !== -1) { - if (ua.indexOf('chrome') > -1) { - // Chrome - } else { - return this.skip(); - } +it('updates comment icon height when commented text is moved to another line', async function () { + // don't run this test in safari. borrowed from + // https://stackoverflow.com/questions/7944460/detect-safari-browser + const ua = navigator.userAgent.toLowerCase(); + if (ua.indexOf('safari') !== -1) { + if (ua.indexOf('chrome') > -1) { + // Chrome + } else { + return this.skip(); } + } - await finishTestIfIconsAreNotEnabled(async () => { - const inner$ = helper.padInner$; - const outer$ = helper.padOuter$; - const commentId = await getCommentId(); - - // adds some new lines on the beginning of the text - const $firstTextElement = inner$('div').first(); - $firstTextElement.sendkeys('{leftarrow}{enter}{enter}'); - - // wait until the new lines are split into separated .ace-line's - await helper.waitForPromise(() => inner$('div').length > 2); - - // wait until comment is visible again - await helper.waitForPromise(() => { - const $commentIcons = outer$('#commentIcons .comment-icon:visible'); - return $commentIcons.length !== 0; - }); - - // check height is the same - const $commentIcon = outer$(`#commentIcons #icon-${commentId}`); - const $commentedText = inner$(`.${commentId}`); - // all icons are +5px down to adjust position - const expectedTop = $commentedText.offset().top + 5; - expect($commentIcon.offset().top).to.be(expectedTop); - }); - }); + const inner$ = helper.padInner$; + const outer$ = helper.padOuter$; + const commentId = await getCommentId(); - it('shows comment when user clicks on comment icon', async function () { - // we only run test if icons are enabled - await finishTestIfIconsAreNotEnabled(async () => { - const outer$ = helper.padOuter$; - const commentId = await getCommentId(); + // adds some new lines on the beginning of the text + const $firstTextElement = inner$('div').first(); + $firstTextElement.sendkeys('{leftarrow}{enter}{enter}'); - // click on the icon - const $commentIcon = outer$(`#commentIcons #icon-${commentId}`).first(); - $commentIcon.click(); + // wait until the new lines are split into separated .ace-line's + await helper.waitForPromise(() => inner$('div').length > 2); - // check sidebar comment is visible - const $openedSidebarComments = outer$('#comments .sidebar-comment:visible'); - expect($openedSidebarComments.length).to.be(1); - }); + // wait until comment is visible again + await helper.waitForPromise(() => { + const $commentIcons = outer$('#commentIcons .comment-icon:visible'); + return $commentIcons.length !== 0; }); - it('hides comment when user clicks on comment icon twice', async function () { - // don't run this test in safari. borrowed from - // https://stackoverflow.com/questions/7944460/detect-safari-browser - const ua = navigator.userAgent.toLowerCase(); - if (ua.indexOf('safari') !== -1) { - if (ua.indexOf('chrome') > -1) { - // Chrome - } else { - return this.skip(); - } + // check height is the same + const $commentIcon = outer$(`#commentIcons #icon-${commentId}`); + const $commentedText = inner$(`.${commentId}`); + // all icons are +5px down to adjust position + const expectedTop = $commentedText.offset().top + 5; + expect($commentIcon.offset().top).to.be(expectedTop); +}); + +it('shows comment when user clicks on comment icon', async function () { + const outer$ = helper.padOuter$; + const commentId = await getCommentId(); + + // click on the icon + const $commentIcon = outer$(`#commentIcons #icon-${commentId}`).first(); + $commentIcon.click(); + + // check sidebar comment is visible + const $openedSidebarComments = outer$('#comments .sidebar-comment:visible'); + expect($openedSidebarComments.length).to.be(1); +}); + +it('hides comment when user clicks on comment icon twice', async function () { + // don't run this test in safari. borrowed from + // https://stackoverflow.com/questions/7944460/detect-safari-browser + const ua = navigator.userAgent.toLowerCase(); + if (ua.indexOf('safari') !== -1) { + if (ua.indexOf('chrome') > -1) { + // Chrome + } else { + return this.skip(); } + } - // we only run test if icons are enabled - await finishTestIfIconsAreNotEnabled(async () => { - const outer$ = helper.padOuter$; - const commentId = await getCommentId(); + const outer$ = helper.padOuter$; + const commentId = await getCommentId(); - // click on the icon to open, then click again to close - const $commentIcon = outer$(`#commentIcons #icon-${commentId}`).first(); - $commentIcon.click(); - $commentIcon.click(); + // click on the icon to open, then click again to close + const $commentIcon = outer$(`#commentIcons #icon-${commentId}`).first(); + $commentIcon.click(); + $commentIcon.click(); - // check sidebar comment is not visible - const $openedSidebarComments = outer$('#comments .sidebar-comment:visible'); - expect($openedSidebarComments.length).to.be(0); - }); - }); + // check sidebar comment is not visible + const $openedSidebarComments = outer$('#comments .sidebar-comment:visible'); + expect($openedSidebarComments.length).to.be(0); +}); - it('hides comment when user clicks outside of comment box', async function () { - // don't run this test in safari. borrowed from - // https://stackoverflow.com/questions/7944460/detect-safari-browser - const ua = navigator.userAgent.toLowerCase(); - if (ua.indexOf('safari') !== -1) { - if (ua.indexOf('chrome') > -1) { - // Chrome - } else { - return this.skip(); - } +it('hides comment when user clicks outside of comment box', async function () { + // don't run this test in safari. borrowed from + // https://stackoverflow.com/questions/7944460/detect-safari-browser + const ua = navigator.userAgent.toLowerCase(); + if (ua.indexOf('safari') !== -1) { + if (ua.indexOf('chrome') > -1) { + // Chrome + } else { + return this.skip(); } + } - // we only run test if icons are enabled - await finishTestIfIconsAreNotEnabled(async () => { - const outer$ = helper.padOuter$; - const commentId = await getCommentId(); + const outer$ = helper.padOuter$; + const commentId = await getCommentId(); - // click on the icon to open - const $commentIcon = outer$(`#commentIcons #icon-${commentId}`).first(); - $commentIcon.click(); + // click on the icon to open + const $commentIcon = outer$(`#commentIcons #icon-${commentId}`).first(); + $commentIcon.click(); - // click outside the comment to hide it - outer$('#outerdocbody').click(); + // click outside the comment to hide it + outer$('#outerdocbody').click(); - // check sidebar comment is not visible - const $openedSidebarComments = outer$('#comments .sidebar-comment:visible'); - expect($openedSidebarComments.length).to.be(0); - }); - }); + // check sidebar comment is not visible + const $openedSidebarComments = outer$('#comments .sidebar-comment:visible'); + expect($openedSidebarComments.length).to.be(0); +}); - it('hides 1st, shows 2nd comment when user clicks on one then another icon', async function () { - // don't run this test in safari. borrowed from - // https://stackoverflow.com/questions/7944460/detect-safari-browser - const ua = navigator.userAgent.toLowerCase(); - if (ua.indexOf('safari') !== -1) { - if (ua.indexOf('chrome') > -1) { - // Chrome - } else { - return this.skip(); - } +it('hides 1st, shows 2nd comment when user clicks on one then another icon', async function () { + // don't run this test in safari. borrowed from + // https://stackoverflow.com/questions/7944460/detect-safari-browser + const ua = navigator.userAgent.toLowerCase(); + if (ua.indexOf('safari') !== -1) { + if (ua.indexOf('chrome') > -1) { + // Chrome + } else { + return this.skip(); } + } - // we only run test if icons are enabled - await finishTestIfIconsAreNotEnabled(async () => { - const inner$ = helper.padInner$; - const outer$ = helper.padOuter$; - - // add a second line... - const $lastTextElement = inner$('div').last(); - $lastTextElement.sendkeys('Second line{enter}'); - - // wait until the new line is split into a separated .ace-line - await helper.waitForPromise(() => inner$('div').length > 2); - - // ... then add a comment to second line - const $secondLine = inner$('div').eq(1); - $secondLine.sendkeys('{selectall}'); - await addComment('Second Comment'); - - // click on the icon of first comment... - const $firstCommentIcon = outer$(`#commentIcons #icon-${await getCommentId(0)}`).first(); - $firstCommentIcon.click(); - // ... then click on the icon of last comment - const $secondCommentIcon = outer$(`#commentIcons #icon-${await getCommentId(1)}`).first(); - $secondCommentIcon.click(); - - // check modal is visible - const $commentText = outer$('#comments .sidebar-comment:visible .comment-text').text(); - expect($commentText).to.be('Second Comment'); - }); - }); + const inner$ = helper.padInner$; + const outer$ = helper.padOuter$; + + // add a second line... + const $lastTextElement = inner$('div').last(); + $lastTextElement.sendkeys('Second line{enter}'); + + // wait until the new line is split into a separated .ace-line + await helper.waitForPromise(() => inner$('div').length > 2); - /* ********** Helper functions ********** */ - - const createComment = async () => { - const inner$ = helper.padInner$; - - // get the first text element out of the inner iframe - const $firstTextElement = inner$('div').first(); - - // simulate key presses to delete content - $firstTextElement.sendkeys('{selectall}'); // select all - $firstTextElement.sendkeys('{del}'); // clear the first line - $firstTextElement.sendkeys('This content will receive a comment{enter}'); // insert text - // wait until the two lines are split into two .ace-line's - await helper.waitForPromise(() => inner$('div').length > 1); - - // add comment to last line of the text - const $lastTextElement = inner$('div').first(); - $lastTextElement.sendkeys('{selectall}'); // need to select content to add comment to - - await addComment('My comment'); - }; - - // Assumes text is already selected, then add comment to the selected text - const addComment = async (commentText) => { - const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; - - // get original number of comments, so can check if a new comment was created - const numberOfComments = inner$('.comment:visible').length; - - // get the comment button and click it - const $commentButton = chrome$('.addComment'); - $commentButton.click(); - - // fill the comment form and submit it - const $commentField = chrome$('textarea.comment-content'); - $commentField.val(commentText); - // we don't need comment suggestion to be filled for these tests, but here's how to do it: - // var $hasSuggestion = outer$(".suggestion-checkbox"); - // $hasSuggestion.click(); - // var $suggestionField = outer$("textarea.to-value"); - // $suggestionField.val("Change to this suggestion"); - const $submittButton = chrome$('.comment-buttons input[type=submit]'); - $submittButton.click(); - - // wait until comment is created and comment id is set - let running = false; - let success = false; - await helper.waitForPromise(() => { - if (success) return true; - if (!running) { - running = true; - getCommentId(numberOfComments).then((id) => { - running = false; - success = id != null; - }); - } - return success; - }); - }; - - const deleteComment = async () => { - const chrome$ = helper.padChrome$; - const outer$ = helper.padOuter$; - - // click on the delete button - const $deleteButton = outer$('.comment-delete'); - $deleteButton.click(); - - await helper.waitForPromise(() => chrome$('.sidebar-comment').is(':visible') === false); - }; - - - const getCommentId = async (numberOfComments) => { - const nthComment = numberOfComments || 0; - const p = helper.waitFor(() => helper.padInner$); - p.fail(() => {}); // Prevent p from throwing an uncatchable exception on error. - await p; - const inner$ = helper.padInner$; - const comment = inner$('.comment').eq(nthComment); - const cls = comment.attr('class'); - const classCommentId = /(?:^| )(c-[A-Za-z0-9]*)/.exec(cls); - const commentId = (classCommentId) ? classCommentId[1] : null; - return commentId; - }; - - const finishTestIfIconsAreNotEnabled = async (theTest) => { - // #commentIcons will only be inserted if icons are enabled - if (helper.padOuter$('#commentIcons').length !== 0) await theTest(); - }; - - const chooseToShowComments = (shouldShowComments) => { - const chrome$ = helper.padChrome$; - - // click on the settings button to make settings visible - const $settingsButton = chrome$('.buttonicon-settings'); - $settingsButton.click(); - - // check "Show Comments" - const $showComments = chrome$('#options-comments'); - if ($showComments.is(':checked') !== shouldShowComments) $showComments.click(); - - // hide settings again - $settingsButton.click(); - }; - - const enlargeScreen = () => { - $('#iframe-container iframe').css('max-width', '1000px'); - }; + // ... then add a comment to second line + const $secondLine = inner$('div').eq(1); + $secondLine.sendkeys('{selectall}'); + await addComment('Second Comment'); + + // click on the icon of first comment... + const $firstCommentIcon = outer$(`#commentIcons #icon-${await getCommentId(0)}`).first(); + $firstCommentIcon.click(); + // ... then click on the icon of last comment + const $secondCommentIcon = outer$(`#commentIcons #icon-${await getCommentId(1)}`).first(); + $secondCommentIcon.click(); + + // check modal is visible + const $commentText = outer$('#comments .sidebar-comment:visible .comment-text').text(); + expect($commentText).to.be('Second Comment'); }); + +/* ********** Helper functions ********** */ + +const createComment = async () => { + const inner$ = helper.padInner$; + + // get the first text element out of the inner iframe + const $firstTextElement = inner$('div').first(); + + // simulate key presses to delete content + $firstTextElement.sendkeys('{selectall}'); // select all + $firstTextElement.sendkeys('{del}'); // clear the first line + $firstTextElement.sendkeys('This content will receive a comment{enter}'); // insert text + // wait until the two lines are split into two .ace-line's + await helper.waitForPromise(() => inner$('div').length > 1); + + // add comment to last line of the text + const $lastTextElement = inner$('div').first(); + $lastTextElement.sendkeys('{selectall}'); // need to select content to add comment to + + await addComment('My comment'); +}; + +// Assumes text is already selected, then add comment to the selected text +const addComment = async (commentText) => { + const inner$ = helper.padInner$; + const chrome$ = helper.padChrome$; + + // get original number of comments, so can check if a new comment was created + const numberOfComments = inner$('.comment:visible').length; + + // get the comment button and click it + const $commentButton = chrome$('.addComment'); + $commentButton.click(); + + // fill the comment form and submit it + const $commentField = chrome$('textarea.comment-content'); + $commentField.val(commentText); + // we don't need comment suggestion to be filled for these tests, but here's how to do it: + // var $hasSuggestion = outer$(".suggestion-checkbox"); + // $hasSuggestion.click(); + // var $suggestionField = outer$("textarea.to-value"); + // $suggestionField.val("Change to this suggestion"); + const $submittButton = chrome$('.comment-buttons input[type=submit]'); + $submittButton.click(); + + // wait until comment is created and comment id is set + await helper.waitForPromise(async () => await getCommentId(numberOfComments) != null); +}; + +const deleteComment = async () => { + const chrome$ = helper.padChrome$; + const outer$ = helper.padOuter$; + + // click on the delete button + const $deleteButton = outer$('.comment-delete'); + $deleteButton.click(); + + await helper.waitForPromise(() => chrome$('.sidebar-comment').is(':visible') === false); +}; + + +const getCommentId = async (numberOfComments) => { + const nthComment = numberOfComments || 0; + await helper.waitForPromise(() => helper.padInner$); + const inner$ = helper.padInner$; + const comment = inner$('.comment').eq(nthComment); + const cls = comment.attr('class'); + const classCommentId = /(?:^| )(c-[A-Za-z0-9]*)/.exec(cls); + const commentId = (classCommentId) ? classCommentId[1] : null; + return commentId; +}; + +const chooseToShowComments = (shouldShowComments) => { + const chrome$ = helper.padChrome$; + + // click on the settings button to make settings visible + const $settingsButton = chrome$('.buttonicon-settings'); + $settingsButton.click(); + + // check "Show Comments" + const $showComments = chrome$('#options-comments'); + if ($showComments.is(':checked') !== shouldShowComments) $showComments.click(); + + // hide settings again + $settingsButton.click(); +}; + +const enlargeScreen = () => { + $('#iframe-container iframe').css('max-width', '1000px'); +}; diff --git a/static/tests/frontend/specs/commentSuggestion.js b/static/tests/frontend/specs/commentSuggestion.js index 6b3ea55d..1c12dc0c 100644 --- a/static/tests/frontend/specs/commentSuggestion.js +++ b/static/tests/frontend/specs/commentSuggestion.js @@ -1,66 +1,83 @@ 'use strict'; -describe('ep_comments_page - Comment Suggestion', function () { - // create a new pad before each test run - beforeEach(function (cb) { - helper.newPad(cb); - this.timeout(60000); - }); +const utils = require('../utils'); - it('Fills suggestion Change From field when adding a comment with suggestion', async function () { - const chrome$ = helper.padChrome$; +// create a new pad before each test run +beforeEach(async function () { + this.timeout(60000); + await utils.aNewPad(); +}); - // As in the function openCommentFormWithSuggestion we send all the text and call 'selectall', - // we select the beginning of line as well. This situation does not happen in the browser, it's - // not possible to select the beginning of first line of a selection. To fix this we add a first - // text without line attribute, in this case a , to avoid select a '*' - const targetText = 'A
  • text with
  • line attributes
'; +it('Fills suggestion Change From field when adding a comment with suggestion', async function () { + const chrome$ = helper.padChrome$; - await openCommentFormWithSuggestion(targetText); - const $suggestionFrom = chrome$('.from-value'); - expect($suggestionFrom.text()).to.be('A\n text with\n line attributes'); - }); + // As in the function openCommentFormWithSuggestion we send all the text and call 'selectall', + // we select the beginning of line as well. This situation does not happen in the browser, it's + // not possible to select the beginning of first line of a selection. To fix this we add a first + // text without line attribute, in this case a , to avoid select a '*' + const targetText = 'A
  • text with
  • line attributes
'; + + await openCommentFormWithSuggestion(targetText); + const $suggestionFrom = chrome$('.from-value'); + expect($suggestionFrom.text()).to.be('A\n text with\n line attributes'); +}); + +it('Cancel suggestion and try again fills suggestion Change From field', async function () { + const outer$ = helper.padOuter$; + const chrome$ = helper.padChrome$; - it('Cancel suggestion and try again fills suggestion Change From field', async function () { - const outer$ = helper.padOuter$; - const chrome$ = helper.padChrome$; + await openCommentFormWithSuggestion('This content will receive a comment'); - await openCommentFormWithSuggestion('This content will receive a comment'); + // cancel + const $cancelButton = chrome$('#comment-reset'); + $cancelButton.click(); - // cancel - const $cancelButton = chrome$('#comment-reset'); - $cancelButton.click(); + // wait for comment form to close + await helper.waitForPromise(() => outer$('#newComments.active').length === 0); + await openCommentFormWithSuggestion('New target for comment'); - // wait for comment form to close - await helper.waitForPromise(() => outer$('#newComments.active').length === 0); - await openCommentFormWithSuggestion('New target for comment'); + const $suggestionFrom = chrome$('.from-value'); + expect($suggestionFrom.text()).to.be('New target for comment'); +}); + +it('Fills suggestion Change From field, adds sugestion', async function () { + const outer$ = helper.padOuter$; + const inner$ = helper.padInner$; + const chrome$ = helper.padChrome$; + const origText = 'This content will receive a comment'; + const suggestedText = 'amp: & dq: " sq: \' lt: < gt: > bs: \\ end'; + await openCommentFormWithSuggestion(origText); - const $suggestionFrom = chrome$('.from-value'); - expect($suggestionFrom.text()).to.be('New target for comment'); + await helper.waitForPromise(() => chrome$('#newComment.popup-show').is(':visible')); + chrome$('#newComment').find('textarea.comment-content').val('A new comment text'); + chrome$('#newComment').find('suggestion-checkbox').click(); + let newCommentSuggestion; + await helper.waitForPromise(() => { + newCommentSuggestion = chrome$('#newComment').find('textarea.to-value'); + return newCommentSuggestion.length > 0 && newCommentSuggestion.is(':visible'); }); + newCommentSuggestion.val(suggestedText); + chrome$('#comment-create-btn').click(); - it('Fills suggestion Change From field, adds sugestion', async function () { - const outer$ = helper.padOuter$; - const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; - const suggestedText = 'A new suggested text'; - await openCommentFormWithSuggestion('This content will receive a comment'); - - await helper.waitForPromise(() => chrome$('#newComment.popup-show').is(':visible')); - chrome$('#newComment').find('textarea.comment-content').val('A new comment text'); - chrome$('#newComment').find('textarea.to-value').val(suggestedText); - chrome$('#comment-create-btn').click(); - await helper.waitForPromise(() => inner$('div').first().find('.comment').length); - let comment$ = inner$('div').first().find('.comment'); - comment$.click(); - await helper.waitForPromise(() => { - outer$('.approve-suggestion-btn:visible').click(); - return true; - }); - comment$ = inner$('div').first().find('.comment'); - await helper.waitForPromise(() => comment$.text() === suggestedText); - expect(comment$.text()).to.be(suggestedText); + let commentedText$; + await helper.waitForPromise(() => { + commentedText$ = inner$('div').first().find('.comment'); + return commentedText$.length > 0; }); + commentedText$.click(); + let comment$; + await helper.waitForPromise(() => { + comment$ = outer$('.comment-container'); + const fd$ = comment$.find('.full-display-content'); + return comment$.length > 0 && fd$.length > 0 && fd$.is(':visible'); + }); + await helper.waitForPromise( + () => comment$.find('.comment-title-wrapper .from-label').text().includes(suggestedText)); + + outer$('.approve-suggestion-btn:visible').click(); + commentedText$ = inner$('div').first().find('.comment'); + await helper.waitForPromise( + () => inner$('div').first().find('.comment').text() === suggestedText); }); const openCommentFormWithSuggestion = async (targetText) => { diff --git a/static/tests/frontend/specs/comment_l10n.js b/static/tests/frontend/specs/comment_l10n.js index 0f31677d..12786bfd 100644 --- a/static/tests/frontend/specs/comment_l10n.js +++ b/static/tests/frontend/specs/comment_l10n.js @@ -1,137 +1,137 @@ 'use strict'; -describe('ep_comments_page - Comment Localization', function () { - // create a new pad with comment before each test run - beforeEach(function (cb) { - helper.newPad(() => { - createComment(() => { - // ensure we start on the default language - changeEtherpadLanguageTo('en', cb); - }); - }); - this.timeout(60000); - }); - - // ensure we go back to English to avoid breaking other tests: - after(function (cb) { - changeEtherpadLanguageTo('en', cb); - }); - - it('uses default values when language was not localized yet', function (done) { - changeEtherpadLanguageTo('oc', () => { - const outer$ = helper.padOuter$; - - // get the title of the comment - const $changeToLabel = outer$('.comment-suggest').first(); - expect($changeToLabel.text()).to.be( - ' Include suggested change '); - - done(); - }); - }); - - it('localizes comment when Etherpad language is changed', function (done) { - changeEtherpadLanguageTo('pt-br', () => { - const outer$ = helper.padOuter$; - const commentId = getCommentId(); - - // get the 'Suggested Change' label - const $changeToLabel = outer$(`#${commentId} .from-label`).first(); - expect($changeToLabel.text()).to.be('Sugerir alteração de'); - - done(); - }); - }); - - it("localizes 'new comment' form when Etherpad language is changed", function (done) { - // make sure form was created before changing the language - const inner$ = helper.padInner$; - const outer$ = helper.padOuter$; - const chrome$ = helper.padChrome$; - - // get the first text element out of the inner iframe - const $firstTextElement = inner$('div').first(); - - // get the comment button and click it - $firstTextElement.sendkeys('{selectall}'); // needs to select content to add comment to - const $commentButton = chrome$('.addComment'); - $commentButton.click(); - - changeEtherpadLanguageTo('pt-br', () => { - // get the 'Include suggested change' label - const $changeToLabel = outer$('.new-comment label.label-suggestion-checkbox').first(); - expect($changeToLabel.text()).to.be('Incluir alteração sugerida'); - - done(); - }); - }); - - /* ********** Helper functions ********** */ - - const createComment = (callback) => { - const inner$ = helper.padInner$; - const outer$ = helper.padOuter$; - const chrome$ = helper.padChrome$; - - // get the first text element out of the inner iframe - const $firstTextElement = inner$('div').first(); - - // simulate key presses to delete content - $firstTextElement.sendkeys('{selectall}'); // select all - $firstTextElement.sendkeys('{del}'); // clear the first line - $firstTextElement.sendkeys('This content will receive a comment'); // insert text - - // get the comment button and click it - $firstTextElement.sendkeys('{selectall}'); // needs to select content to add comment to - const $commentButton = chrome$('.addComment'); - $commentButton.click(); - - // fill the comment form and submit it - const $commentField = chrome$('textarea.comment-content'); - $commentField.val('My comment'); - const $hasSuggestion = outer$('.suggestion-checkbox'); - $hasSuggestion.click(); - const $suggestionField = outer$('textarea.to-value'); - $suggestionField.val('Change to this suggestion'); - const $submittButton = chrome$('.comment-buttons input[type=submit]'); - $submittButton.click(); - - // wait until comment is created and comment id is set - helper.waitFor(() => getCommentId() != null) - .done(callback); - }; +const utils = require('../utils'); - const changeEtherpadLanguageTo = (lang, callback) => { - const boldTitles = { - 'en': 'Bold (Ctrl+B)', - 'pt-br': 'Negrito (Ctrl-B)', - 'oc': 'Gras (Ctrl-B)', - }; - const chrome$ = helper.padChrome$; +const commentedText = 'This content will receive a comment'; +const suggestedText = 'Change to this suggestion'; - // click on the settings button to make settings visible - const $settingsButton = chrome$('.buttonicon-settings'); - $settingsButton.click(); +// create a new pad with comment before each test run +beforeEach(async function () { + this.timeout(60000); + await utils.aNewPad(); + await createComment(); + await changeEtherpadLanguageTo('en'); +}); - // select the language - const $language = chrome$('#languagemenu'); - $language.val(lang); - $language.change(); +// ensure we go back to English to avoid breaking other tests: +after(async function () { + await changeEtherpadLanguageTo('en'); +}); - // hide settings again - $settingsButton.click(); +it('uses default values when language was not localized yet', async function () { + await changeEtherpadLanguageTo('oc'); + const outer$ = helper.padOuter$; - helper.waitFor(() => chrome$('.buttonicon-bold').parent()[0].title === boldTitles[lang]) - .done(callback); - }; + // get the title of the comment + const $changeToLabel = outer$('.comment-suggest').first(); + expect($changeToLabel.text()).to.be( + ' Include suggested change '); +}); - const getCommentId = () => { - const inner$ = helper.padInner$; - const comment = inner$('.comment').first(); - const cls = comment.attr('class'); - const classCommentId = /(?:^| )(c-[A-Za-z0-9]*)/.exec(cls); - const commentId = (classCommentId) ? classCommentId[1] : null; +it('localizes comment when Etherpad language is changed', async function () { + await changeEtherpadLanguageTo('pt-br'); + const outer$ = helper.padOuter$; + const commentId = getCommentId(); - return commentId; - }; + // get the 'Suggested Change' label + const $changeToLabel = outer$(`#${commentId} .from-label`).first(); + expect($changeToLabel.text()) + .to.be(`Alteração sugerida de "${commentedText}" para "${suggestedText}"`); }); + +it("localizes 'new comment' form when Etherpad language is changed", async function () { + // make sure form was created before changing the language + const inner$ = helper.padInner$; + const outer$ = helper.padOuter$; + const chrome$ = helper.padChrome$; + + // get the first text element out of the inner iframe + const $firstTextElement = inner$('div').first(); + + // get the comment button and click it + $firstTextElement.sendkeys('{selectall}'); // needs to select content to add comment to + const $commentButton = chrome$('.addComment'); + $commentButton.click(); + + await changeEtherpadLanguageTo('pt-br'); + // get the 'Include suggested change' label + const $changeToLabel = outer$('.new-comment label.label-suggestion-checkbox').first(); + expect($changeToLabel.text()).to.be('Incluir alteração sugerida'); +}); + +/* ********** Helper functions ********** */ + +const createComment = async () => { + const inner$ = helper.padInner$; + const chrome$ = helper.padChrome$; + + // Returns the first line div. Must be a function because Etherpad might replace the div with a + // new div if the content changes. + const $firstTextElement = () => { + const $div = inner$('div').first(); + expect($div.length).to.be(1); + return $div; + }; + + // simulate key presses to delete content + $firstTextElement().sendkeys('{selectall}'); // select all + $firstTextElement().sendkeys('{del}'); // clear the first line + $firstTextElement().sendkeys(commentedText); // insert text + + // get the comment button and click it + $firstTextElement().sendkeys('{selectall}'); // needs to select content to add comment to + const $commentButton = chrome$('.addComment'); + expect($commentButton.length).to.be(1); + $commentButton.click(); + + // fill the comment form and submit it + const $commentField = chrome$('textarea.comment-content'); + expect($commentField.length).to.be(1); + $commentField.val('My comment'); + const $hasSuggestion = chrome$('#newComment .suggestion-checkbox'); + expect($hasSuggestion.length).to.be(1); + $hasSuggestion.click(); + const $suggestionField = chrome$('textarea.to-value'); + expect($suggestionField.length).to.be(1); + $suggestionField.val(suggestedText); + const $submittButton = chrome$('.comment-buttons input[type=submit]'); + expect($submittButton.length).to.be(1); + $submittButton.click(); + + // wait until comment is created and comment id is set + await helper.waitForPromise(() => getCommentId() != null); +}; + +const changeEtherpadLanguageTo = async (lang) => { + const boldTitles = { + 'en': 'Bold (Ctrl+B)', + 'pt-br': 'Negrito (Ctrl-B)', + 'oc': 'Gras (Ctrl-B)', + }; + const chrome$ = helper.padChrome$; + + // click on the settings button to make settings visible + const $settingsButton = chrome$('.buttonicon-settings'); + $settingsButton.click(); + + // select the language + const $language = chrome$('#languagemenu'); + $language.val(lang); + $language.change(); + + // hide settings again + $settingsButton.click(); + + await helper.waitForPromise( + () => chrome$('.buttonicon-bold').parent()[0].title === boldTitles[lang]); +}; + +const getCommentId = () => { + const inner$ = helper.padInner$; + const comment = inner$('.comment'); + if (comment.length === 0) return null; + for (const cls of comment[0].classList) { + if (cls.startsWith('c-')) return cls; + } + return null; +}; diff --git a/static/tests/frontend/specs/newComment.js b/static/tests/frontend/specs/newComment.js new file mode 100644 index 00000000..d979c4f4 --- /dev/null +++ b/static/tests/frontend/specs/newComment.js @@ -0,0 +1,18 @@ +'use strict'; + +const utils = require('../utils'); + +before(async function () { + await utils.aNewPad(); + helper.padInner$('div').first() + .sendkeys('{selectall}') + .sendkeys('{del}') + .text('commented text') + .sendkeys('{selectall}'); +}); + +it('new comment button focuses on comment textarea', async function () { + helper.padChrome$('.addComment').click(); + expect(helper.padChrome$.document.activeElement) + .to.be(helper.padChrome$('#newComment').find('.comment-content')[0]); +}); diff --git a/static/tests/frontend/specs/timeFormat.js b/static/tests/frontend/specs/timeFormat.js index b451bac2..2777a67b 100644 --- a/static/tests/frontend/specs/timeFormat.js +++ b/static/tests/frontend/specs/timeFormat.js @@ -1,266 +1,266 @@ 'use strict'; -describe('ep_comments_page - Time Formatting', function () { - let moment; - let originalLanguage = null; +const utils = require('../utils'); - before(async function () { - this.timeout(60000); - await new Promise((resolve) => helper.newPad(resolve)); - moment = helper.padChrome$.window.require('ep_comments_page/static/js/moment-with-locales.min'); - moment.relativeTimeThreshold('ss', 0); - }); - - after(async function () { - // Restore the language to avoid breaking other tests. - if (originalLanguage != null) await changeLanguageTo(originalLanguage); - }); +let moment; +let originalLanguage = null; - for (const [lang, description] of Object.entries({ - en: 'English', - // See https://translatewiki.net/wiki/FAQ#Special_private_language_codes_qqq,_qqx. - qqq: 'a language that moment.js does not support', - })) { - describe(`in ${description}`, function () { - before(async function () { - // First switch to a supported language that is not 'en' so that we know when the language - // change has taken effect. (This is important because using an unsupported language will - // cause moment.js to fall back to 'en'.) - await changeLanguageTo('pt-br'); - await changeLanguageTo(lang); - }); - - it('returns "12 seconds ago" when time is 12 seconds in the past', async function () { - expect(moment(secondsInThePast(12)).fromNow()).to.be('12 seconds ago'); - }); - - it('returns "in 12 seconds" when time is 12 seconds in the future', async function () { - expect(moment(secondsInTheFuture(12)).fromNow()).to.be('in 12 seconds'); - }); - - it('returns "a minute ago" when time is 75 seconds in the past', async function () { - expect(moment(secondsInThePast(75)).fromNow()).to.be('a minute ago'); - }); - - it('returns "in a minute" when time is 75 seconds in the future', async function () { - expect(moment(secondsInTheFuture(75)).fromNow()).to.be('in a minute'); - }); - - it('returns "17 minutes ago" when time is 17+ε minutes in the past', async function () { - expect(moment(secondsInThePast(minutes(17) + 2)).fromNow()).to.be('17 minutes ago'); - }); - - it('returns "in 17 minutes" when time is 17+ε minutes in the future', async function () { - expect(moment(secondsInTheFuture(minutes(17) + 2)).fromNow()).to.be('in 17 minutes'); - }); - - it('returns "an hour ago" when time is 1+ε hour in the past', async function () { - expect(moment(secondsInThePast(hours(1) + 3)).fromNow()).to.be('an hour ago'); - }); - - it('returns "in an hour" when time is 1+ε hour in the future', async function () { - expect(moment(secondsInTheFuture(hours(1) + 3)).fromNow()).to.be('in an hour'); - }); - - it('returns "2 hours ago" when time is 2+ε hours in the past', async function () { - expect(moment(secondsInThePast(hours(2) + 4)).fromNow()).to.be('2 hours ago'); - }); - - it('returns "in 2 hours" when time is 2+ε hours in the future', async function () { - expect(moment(secondsInTheFuture(hours(2) + 4)).fromNow()).to.be('in 2 hours'); - }); - - it('returns "a day ago" when time is 24+ε hours in the past', async function () { - expect(moment(secondsInThePast(hours(24) + 5)).fromNow()).to.be('a day ago'); - }); - - it('returns "in a day" when time is 24+ε hours in the future', async function () { - expect(moment(secondsInTheFuture(hours(24) + 5)).fromNow()).to.be('in a day'); - }); - - it('returns "6 days ago" when time is 6+ε days in the past', async function () { - expect(moment(secondsInThePast(days(6) + 6)).fromNow()).to.be('6 days ago'); - }); - - it('returns "in 6 days" when time is 6+ε days in the future', async function () { - expect(moment(secondsInTheFuture(days(6) + 6)).fromNow()).to.be('in 6 days'); - }); - - it('returns "7 days ago" when time is 7+ε days in the past', async function () { - expect(moment(secondsInThePast(days(7) + 7)).fromNow()).to.be('7 days ago'); - }); - - it('returns "in 7 days" when time is 7+ε days in the future', async function () { - expect(moment(secondsInTheFuture(days(7) + 7)).fromNow()).to.be('in 7 days'); - }); - - it('returns "14 days ago" when time is 2+ε weeks in the past', async function () { - expect(moment(secondsInThePast(weeks(2) + 8)).fromNow()).to.be('14 days ago'); - }); - - it('returns "in 14 days" when time is 2+ε weeks in the future', async function () { - expect(moment(secondsInTheFuture(weeks(2) + 8)).fromNow()).to.be('in 14 days'); - }); - - it('returns "a month ago" when time is 4+ε weeks in the past', async function () { - expect(moment(secondsInThePast(weeks(4) + 9)).fromNow()).to.be('a month ago'); - }); - - it('returns "in a month" when time is 4+ε weeks in the future', async function () { - expect(moment(secondsInTheFuture(weeks(4) + 9)).fromNow()).to.be('in a month'); - }); - - it('returns "8 months ago" when time is 9+ε months in the past', async function () { - expect(moment(secondsInThePast(months(9) + 10)).fromNow()).to.be('8 months ago'); - }); - - it('returns "in 8 months" when time is 9+ε months in the future', async function () { - expect(moment(secondsInTheFuture(months(9) + 10)).fromNow()).to.be('in 8 months'); - }); - - it('returns "a year ago" when time is 12+ε months in the past', async function () { - expect(moment(secondsInThePast(months(12) + 11)).fromNow()).to.be('a year ago'); - }); - - it('returns "in a year" when time is 12+ε months in the future', async function () { - expect(moment(secondsInTheFuture(months(12) + 11)).fromNow()).to.be('in a year'); - }); - - it('returns "14 years ago" when time is 15+ε years in the past', async function () { - expect(moment(secondsInThePast(years(15) + 12)).fromNow()).to.be('14 years ago'); - }); +before(async function () { + this.timeout(60000); + await utils.aNewPad(); + moment = helper.padChrome$.window.require('ep_comments_page/static/js/moment-with-locales.min'); + moment.relativeTimeThreshold('ss', 0); +}); - it('returns " in 14 years" when time is 15+ε years in the future', async function () { - expect(moment(secondsInTheFuture(years(15) + 12)).fromNow()).to.be('in 14 years'); - }); - }); - } +after(async function () { + // Restore the language to avoid breaking other tests. + if (originalLanguage != null) await changeLanguageTo(originalLanguage); +}); - describe('in Portuguese', function () { +for (const [lang, description] of Object.entries({ + en: 'English', + // See https://translatewiki.net/wiki/FAQ#Special_private_language_codes_qqq,_qqx. + qqq: 'a language that moment.js does not support', +})) { + describe(`in ${description}`, function () { before(async function () { + // First switch to a supported language that is not 'en' so that we know when the language + // change has taken effect. (This is important because using an unsupported language will + // cause moment.js to fall back to 'en'.) await changeLanguageTo('pt-br'); + await changeLanguageTo(lang); }); - it('returns "há 12 segundos" when time is 12 seconds in the past', async function () { - expect(moment(secondsInThePast(12)).fromNow()).to.be('há 12 segundos'); + it('returns "12 seconds ago" when time is 12 seconds in the past', async function () { + expect(moment(secondsInThePast(12)).fromNow()).to.be('12 seconds ago'); }); - it('returns "em 12 segundos" when time is 12 seconds in the future', async function () { - expect(moment(secondsInTheFuture(12)).fromNow()).to.be('em 12 segundos'); + it('returns "in 12 seconds" when time is 12 seconds in the future', async function () { + expect(moment(secondsInTheFuture(12)).fromNow()).to.be('in 12 seconds'); }); - it('returns "há um minuto" when time is 75 seconds in the past', async function () { - expect(moment(secondsInThePast(75)).fromNow()).to.be('há um minuto'); + it('returns "a minute ago" when time is 75 seconds in the past', async function () { + expect(moment(secondsInThePast(75)).fromNow()).to.be('a minute ago'); }); - it('returns "em um minuto" when time is 75 seconds in the future', async function () { - expect(moment(secondsInTheFuture(75)).fromNow()).to.be('em um minuto'); + it('returns "in a minute" when time is 75 seconds in the future', async function () { + expect(moment(secondsInTheFuture(75)).fromNow()).to.be('in a minute'); }); - it('returns "há 17 minutos" when time is 17+ε minutes in the past', async function () { - expect(moment(secondsInThePast(minutes(17) + 2)).fromNow()).to.be('há 17 minutos'); + it('returns "17 minutes ago" when time is 17+ε minutes in the past', async function () { + expect(moment(secondsInThePast(minutes(17) + 2)).fromNow()).to.be('17 minutes ago'); }); - it('returns "em 17 minutos" when time is 17+ε minutes in the future', async function () { - expect(moment(secondsInTheFuture(minutes(17) + 2)).fromNow()).to.be('em 17 minutos'); + it('returns "in 17 minutes" when time is 17+ε minutes in the future', async function () { + expect(moment(secondsInTheFuture(minutes(17) + 2)).fromNow()).to.be('in 17 minutes'); }); - it('returns "há uma hora" when time is 1+ε hour in the past', async function () { - expect(moment(secondsInThePast(hours(1) + 3)).fromNow()).to.be('há uma hora'); + it('returns "an hour ago" when time is 1+ε hour in the past', async function () { + expect(moment(secondsInThePast(hours(1) + 3)).fromNow()).to.be('an hour ago'); }); - it('returns "em uma hora" when time is 1+ε hour in the future', async function () { - expect(moment(secondsInTheFuture(hours(1) + 3)).fromNow()).to.be('em uma hora'); + it('returns "in an hour" when time is 1+ε hour in the future', async function () { + expect(moment(secondsInTheFuture(hours(1) + 3)).fromNow()).to.be('in an hour'); }); - it('returns "há 2 horas" when time is 2+ε hours in the past', async function () { - expect(moment(secondsInThePast(hours(2) + 4)).fromNow()).to.be('há 2 horas'); + it('returns "2 hours ago" when time is 2+ε hours in the past', async function () { + expect(moment(secondsInThePast(hours(2) + 4)).fromNow()).to.be('2 hours ago'); }); - it('returns "em 2 horas" when time is 2+ε hours in the future', async function () { - expect(moment(secondsInTheFuture(hours(2) + 4)).fromNow()).to.be('em 2 horas'); + it('returns "in 2 hours" when time is 2+ε hours in the future', async function () { + expect(moment(secondsInTheFuture(hours(2) + 4)).fromNow()).to.be('in 2 hours'); }); - it('returns "há um dia" when time is 24+ε hours in the past', async function () { - expect(moment(secondsInThePast(hours(24) + 5)).fromNow()).to.be('há um dia'); + it('returns "a day ago" when time is 24+ε hours in the past', async function () { + expect(moment(secondsInThePast(hours(24) + 5)).fromNow()).to.be('a day ago'); }); - it('returns "em um dia" when time is 24+ε hours in the future', async function () { - expect(moment(secondsInTheFuture(hours(24) + 5)).fromNow()).to.be('em um dia'); + it('returns "in a day" when time is 24+ε hours in the future', async function () { + expect(moment(secondsInTheFuture(hours(24) + 5)).fromNow()).to.be('in a day'); }); - it('returns "há 6 dias" when time is 6+ε days in the past', async function () { - expect(moment(secondsInThePast(days(6) + 6)).fromNow()).to.be('há 6 dias'); + it('returns "6 days ago" when time is 6+ε days in the past', async function () { + expect(moment(secondsInThePast(days(6) + 6)).fromNow()).to.be('6 days ago'); }); - it('returns "em 6 dias" when time is 6+ε days in the future', async function () { - expect(moment(secondsInTheFuture(days(6) + 6)).fromNow()).to.be('em 6 dias'); + it('returns "in 6 days" when time is 6+ε days in the future', async function () { + expect(moment(secondsInTheFuture(days(6) + 6)).fromNow()).to.be('in 6 days'); }); - it('returns "há 7 dias" when time is 7+ε days in the past', async function () { - expect(moment(secondsInThePast(days(7) + 7)).fromNow()).to.be('há 7 dias'); + it('returns "7 days ago" when time is 7+ε days in the past', async function () { + expect(moment(secondsInThePast(days(7) + 7)).fromNow()).to.be('7 days ago'); }); - it('returns "em 7 dias" when time is 7+ε days in the future', async function () { - expect(moment(secondsInTheFuture(days(7) + 7)).fromNow()).to.be('em 7 dias'); + it('returns "in 7 days" when time is 7+ε days in the future', async function () { + expect(moment(secondsInTheFuture(days(7) + 7)).fromNow()).to.be('in 7 days'); }); - it('returns "há 14 dias" when time is 2+ε weeks in the past', async function () { - expect(moment(secondsInThePast(weeks(2) + 8)).fromNow()).to.be('há 14 dias'); + it('returns "14 days ago" when time is 2+ε weeks in the past', async function () { + expect(moment(secondsInThePast(weeks(2) + 8)).fromNow()).to.be('14 days ago'); }); - it('returns "em 14 dias" when time is 2+ε weeks in the future', async function () { - expect(moment(secondsInTheFuture(weeks(2) + 8)).fromNow()).to.be('em 14 dias'); + it('returns "in 14 days" when time is 2+ε weeks in the future', async function () { + expect(moment(secondsInTheFuture(weeks(2) + 8)).fromNow()).to.be('in 14 days'); }); - it('returns "há um mês" when time is 4+ε weeks in the past', async function () { - expect(moment(secondsInThePast(weeks(4) + 9)).fromNow()).to.be('há um mês'); + it('returns "a month ago" when time is 4+ε weeks in the past', async function () { + expect(moment(secondsInThePast(weeks(4) + 9)).fromNow()).to.be('a month ago'); }); - it('returns "em um mês" when time is 4+ε weeks in the future', async function () { - expect(moment(secondsInTheFuture(weeks(4) + 9)).fromNow()).to.be('em um mês'); + it('returns "in a month" when time is 4+ε weeks in the future', async function () { + expect(moment(secondsInTheFuture(weeks(4) + 9)).fromNow()).to.be('in a month'); }); - it('returns "há 8 meses" when time is 9+ε months in the past', async function () { - expect(moment(secondsInThePast(months(9) + 10)).fromNow()).to.be('há 8 meses'); + it('returns "8 months ago" when time is 9+ε months in the past', async function () { + expect(moment(secondsInThePast(months(9) + 10)).fromNow()).to.be('8 months ago'); }); - it('returns "em 8 meses" when time is 9+ε months in the future', async function () { - expect(moment(secondsInTheFuture(months(9) + 10)).fromNow()).to.be('em 8 meses'); + it('returns "in 8 months" when time is 9+ε months in the future', async function () { + expect(moment(secondsInTheFuture(months(9) + 10)).fromNow()).to.be('in 8 months'); }); - it('returns "há um ano" when time is 12+ε months in the past', async function () { - expect(moment(secondsInThePast(months(12) + 11)).fromNow()).to.be('há um ano'); + it('returns "a year ago" when time is 12+ε months in the past', async function () { + expect(moment(secondsInThePast(months(12) + 11)).fromNow()).to.be('a year ago'); }); - it('returns "em um ano" when time is 12+ε months in the future', async function () { - expect(moment(secondsInTheFuture(months(12) + 11)).fromNow()).to.be('em um ano'); + it('returns "in a year" when time is 12+ε months in the future', async function () { + expect(moment(secondsInTheFuture(months(12) + 11)).fromNow()).to.be('in a year'); }); - it('returns "há 14 anos" when time is 15+ε years in the past', async function () { - expect(moment(secondsInThePast(years(15) + 12)).fromNow()).to.be('há 14 anos'); + it('returns "14 years ago" when time is 15+ε years in the past', async function () { + expect(moment(secondsInThePast(years(15) + 12)).fromNow()).to.be('14 years ago'); }); - it('returns "em 14 anos" when time is 15+ε years in the future', async function () { - expect(moment(secondsInTheFuture(years(15) + 12)).fromNow()).to.be('em 14 anos'); + it('returns " in 14 years" when time is 15+ε years in the future', async function () { + expect(moment(secondsInTheFuture(years(15) + 12)).fromNow()).to.be('in 14 years'); }); }); +} + +describe('in Portuguese', function () { + before(async function () { + await changeLanguageTo('pt-br'); + }); + + it('returns "há 12 segundos" when time is 12 seconds in the past', async function () { + expect(moment(secondsInThePast(12)).fromNow()).to.be('há 12 segundos'); + }); + + it('returns "em 12 segundos" when time is 12 seconds in the future', async function () { + expect(moment(secondsInTheFuture(12)).fromNow()).to.be('em 12 segundos'); + }); + + it('returns "há um minuto" when time is 75 seconds in the past', async function () { + expect(moment(secondsInThePast(75)).fromNow()).to.be('há um minuto'); + }); + + it('returns "em um minuto" when time is 75 seconds in the future', async function () { + expect(moment(secondsInTheFuture(75)).fromNow()).to.be('em um minuto'); + }); + + it('returns "há 17 minutos" when time is 17+ε minutes in the past', async function () { + expect(moment(secondsInThePast(minutes(17) + 2)).fromNow()).to.be('há 17 minutos'); + }); + + it('returns "em 17 minutos" when time is 17+ε minutes in the future', async function () { + expect(moment(secondsInTheFuture(minutes(17) + 2)).fromNow()).to.be('em 17 minutos'); + }); + + it('returns "há uma hora" when time is 1+ε hour in the past', async function () { + expect(moment(secondsInThePast(hours(1) + 3)).fromNow()).to.be('há uma hora'); + }); + + it('returns "em uma hora" when time is 1+ε hour in the future', async function () { + expect(moment(secondsInTheFuture(hours(1) + 3)).fromNow()).to.be('em uma hora'); + }); + + it('returns "há 2 horas" when time is 2+ε hours in the past', async function () { + expect(moment(secondsInThePast(hours(2) + 4)).fromNow()).to.be('há 2 horas'); + }); + + it('returns "em 2 horas" when time is 2+ε hours in the future', async function () { + expect(moment(secondsInTheFuture(hours(2) + 4)).fromNow()).to.be('em 2 horas'); + }); + + it('returns "há um dia" when time is 24+ε hours in the past', async function () { + expect(moment(secondsInThePast(hours(24) + 5)).fromNow()).to.be('há um dia'); + }); + + it('returns "em um dia" when time is 24+ε hours in the future', async function () { + expect(moment(secondsInTheFuture(hours(24) + 5)).fromNow()).to.be('em um dia'); + }); + + it('returns "há 6 dias" when time is 6+ε days in the past', async function () { + expect(moment(secondsInThePast(days(6) + 6)).fromNow()).to.be('há 6 dias'); + }); + + it('returns "em 6 dias" when time is 6+ε days in the future', async function () { + expect(moment(secondsInTheFuture(days(6) + 6)).fromNow()).to.be('em 6 dias'); + }); + + it('returns "há 7 dias" when time is 7+ε days in the past', async function () { + expect(moment(secondsInThePast(days(7) + 7)).fromNow()).to.be('há 7 dias'); + }); + + it('returns "em 7 dias" when time is 7+ε days in the future', async function () { + expect(moment(secondsInTheFuture(days(7) + 7)).fromNow()).to.be('em 7 dias'); + }); + + it('returns "há 14 dias" when time is 2+ε weeks in the past', async function () { + expect(moment(secondsInThePast(weeks(2) + 8)).fromNow()).to.be('há 14 dias'); + }); + + it('returns "em 14 dias" when time is 2+ε weeks in the future', async function () { + expect(moment(secondsInTheFuture(weeks(2) + 8)).fromNow()).to.be('em 14 dias'); + }); - /* ********** Helper functions ********** */ - - const secondsInThePast = (seconds) => Date.now() - seconds * 1000; - const secondsInTheFuture = (seconds) => Date.now() + seconds * 1000; - const minutes = (count) => 60 * count; - const hours = (count) => 60 * minutes(count); - const days = (count) => 24 * hours(count); - const weeks = (count) => 7 * days(count); - const months = (count) => 4 * weeks(count); - const years = (count) => 12 * months(count); - - const changeLanguageTo = async (lang) => { - if (originalLanguage == null) originalLanguage = helper.padChrome$.window.html10n.getLanguage(); - expect(helper.padChrome$(`#languagemenu [value=${lang}]`).length).to.be(1); - helper.padChrome$('#languagemenu').val(lang).trigger('change'); - await helper.waitForPromise(() => moment.locale() === (lang === 'qqq' ? 'en' : lang)); - }; + it('returns "há um mês" when time is 4+ε weeks in the past', async function () { + expect(moment(secondsInThePast(weeks(4) + 9)).fromNow()).to.be('há um mês'); + }); + + it('returns "em um mês" when time is 4+ε weeks in the future', async function () { + expect(moment(secondsInTheFuture(weeks(4) + 9)).fromNow()).to.be('em um mês'); + }); + + it('returns "há 8 meses" when time is 9+ε months in the past', async function () { + expect(moment(secondsInThePast(months(9) + 10)).fromNow()).to.be('há 8 meses'); + }); + + it('returns "em 8 meses" when time is 9+ε months in the future', async function () { + expect(moment(secondsInTheFuture(months(9) + 10)).fromNow()).to.be('em 8 meses'); + }); + + it('returns "há um ano" when time is 12+ε months in the past', async function () { + expect(moment(secondsInThePast(months(12) + 11)).fromNow()).to.be('há um ano'); + }); + + it('returns "em um ano" when time is 12+ε months in the future', async function () { + expect(moment(secondsInTheFuture(months(12) + 11)).fromNow()).to.be('em um ano'); + }); + + it('returns "há 14 anos" when time is 15+ε years in the past', async function () { + expect(moment(secondsInThePast(years(15) + 12)).fromNow()).to.be('há 14 anos'); + }); + + it('returns "em 14 anos" when time is 15+ε years in the future', async function () { + expect(moment(secondsInTheFuture(years(15) + 12)).fromNow()).to.be('em 14 anos'); + }); }); + +/* ********** Helper functions ********** */ + +const secondsInThePast = (seconds) => Date.now() - seconds * 1000; +const secondsInTheFuture = (seconds) => Date.now() + seconds * 1000; +const minutes = (count) => 60 * count; +const hours = (count) => 60 * minutes(count); +const days = (count) => 24 * hours(count); +const weeks = (count) => 7 * days(count); +const months = (count) => 4 * weeks(count); +const years = (count) => 12 * months(count); + +const changeLanguageTo = async (lang) => { + if (originalLanguage == null) originalLanguage = helper.padChrome$.window.html10n.getLanguage(); + expect(helper.padChrome$(`#languagemenu [value=${lang}]`).length).to.be(1); + helper.padChrome$('#languagemenu').val(lang).trigger('change'); + await helper.waitForPromise(() => moment.locale() === (lang === 'qqq' ? 'en' : lang)); +}; diff --git a/static/tests/frontend/utils.js b/static/tests/frontend/utils.js new file mode 100644 index 00000000..c7c892a5 --- /dev/null +++ b/static/tests/frontend/utils.js @@ -0,0 +1,14 @@ +'use strict'; + +exports.aNewPad = async (...args) => { + const padId = await helper.aNewPad(...args); + // Most ep_comments_page initialization happens during postAceInit, which runs after + // helper.aNewPad() returns. Wait for initialization to complete to avoid race conditions. + await helper.waitForPromise(async () => { + const {plugins: {ep_comments_page: {initDone} = {}} = {}} = helper.padChrome$.window.pad; + if (!initDone) return false; + await initDone; + return true; + }); + return padId; +}; diff --git a/templates/comments.html b/templates/comments.html index 5b45b8e9..323d61b8 100644 --- a/templates/comments.html +++ b/templates/comments.html @@ -97,9 +97,6 @@

Comment

{{if changeTo}}
- - - Suggested Change From @@ -134,6 +131,19 @@

Comment