mediawiki/extensions/MachineVision: main (log #964847)

sourcepatches

This run took 70 seconds.

$ date
--- stdout ---
Tue Mar 21 20:19:21 UTC 2023

--- end ---
$ git clone file:///srv/git/mediawiki-extensions-MachineVision.git repo --depth=1 -b master
--- stderr ---
Cloning into 'repo'...
--- stdout ---

--- end ---
$ git config user.name libraryupgrader
--- stdout ---

--- end ---
$ git config user.email tools.libraryupgrader@tools.wmflabs.org
--- stdout ---

--- end ---
$ git submodule update --init
--- stdout ---

--- end ---
$ grr init
--- stdout ---
Installed commit-msg hook.

--- end ---
$ git show-ref refs/heads/master
--- stdout ---
4d482918ac3f25c1ba1ad31e8e72f3e47e0e253d refs/heads/master

--- end ---
$ /usr/bin/npm audit --json --legacy-peer-deps
--- stdout ---
{
  "auditReportVersion": 2,
  "vulnerabilities": {
    "ansi-regex": {
      "name": "ansi-regex",
      "severity": "high",
      "isDirect": false,
      "via": [
        {
          "source": 1091190,
          "name": "ansi-regex",
          "dependency": "ansi-regex",
          "title": "Inefficient Regular Expression Complexity in chalk/ansi-regex",
          "url": "https://github.com/advisories/GHSA-93q8-gq69-wqmw",
          "severity": "high",
          "cwe": [
            "CWE-697",
            "CWE-1333"
          ],
          "cvss": {
            "score": 7.5,
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
          },
          "range": ">=5.0.0 <5.0.1"
        }
      ],
      "effects": [],
      "range": "5.0.0",
      "nodes": [
        "node_modules/eslint/node_modules/ansi-regex"
      ],
      "fixAvailable": true
    },
    "async": {
      "name": "async",
      "severity": "high",
      "isDirect": false,
      "via": [
        {
          "source": 1088667,
          "name": "async",
          "dependency": "async",
          "title": "Prototype Pollution in async",
          "url": "https://github.com/advisories/GHSA-fwr7-v2mv-hh25",
          "severity": "high",
          "cwe": [
            "CWE-1321"
          ],
          "cvss": {
            "score": 7.8,
            "vectorString": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"
          },
          "range": ">=3.0.0 <3.2.2"
        }
      ],
      "effects": [],
      "range": "3.0.0 - 3.2.1",
      "nodes": [
        "node_modules/async"
      ],
      "fixAvailable": true
    },
    "babel-core": {
      "name": "babel-core",
      "severity": "high",
      "isDirect": true,
      "via": [
        "babel-register",
        "json5"
      ],
      "effects": [
        "babel-register"
      ],
      "range": "5.8.20 - 7.0.0-beta.3",
      "nodes": [
        "node_modules/babel-core"
      ],
      "fixAvailable": {
        "name": "babel-core",
        "version": "4.7.16",
        "isSemVerMajor": true
      }
    },
    "babel-register": {
      "name": "babel-register",
      "severity": "high",
      "isDirect": false,
      "via": [
        "babel-core"
      ],
      "effects": [
        "babel-core"
      ],
      "range": "*",
      "nodes": [
        "node_modules/babel-register"
      ],
      "fixAvailable": {
        "name": "babel-core",
        "version": "4.7.16",
        "isSemVerMajor": true
      }
    },
    "decode-uri-component": {
      "name": "decode-uri-component",
      "severity": "low",
      "isDirect": false,
      "via": [
        {
          "source": 1088828,
          "name": "decode-uri-component",
          "dependency": "decode-uri-component",
          "title": "decode-uri-component vulnerable to Denial of Service (DoS)",
          "url": "https://github.com/advisories/GHSA-w573-4hg7-7wgq",
          "severity": "low",
          "cwe": [
            "CWE-20"
          ],
          "cvss": {
            "score": 0,
            "vectorString": null
          },
          "range": "<0.2.1"
        }
      ],
      "effects": [],
      "range": "<0.2.1",
      "nodes": [
        "node_modules/decode-uri-component"
      ],
      "fixAvailable": true
    },
    "grunt": {
      "name": "grunt",
      "severity": "high",
      "isDirect": true,
      "via": [
        {
          "source": 1088711,
          "name": "grunt",
          "dependency": "grunt",
          "title": "Path Traversal in Grunt",
          "url": "https://github.com/advisories/GHSA-j383-35pm-c5h4",
          "severity": "moderate",
          "cwe": [
            "CWE-22"
          ],
          "cvss": {
            "score": 5.5,
            "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N"
          },
          "range": "<1.5.2"
        },
        {
          "source": 1089539,
          "name": "grunt",
          "dependency": "grunt",
          "title": "Race Condition in Grunt",
          "url": "https://github.com/advisories/GHSA-rm36-94g8-835r",
          "severity": "high",
          "cwe": [
            "CWE-367"
          ],
          "cvss": {
            "score": 7,
            "vectorString": "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H"
          },
          "range": "<1.5.3"
        }
      ],
      "effects": [],
      "range": "<=1.5.2",
      "nodes": [
        "node_modules/grunt"
      ],
      "fixAvailable": {
        "name": "grunt",
        "version": "1.6.1",
        "isSemVerMajor": false
      }
    },
    "json5": {
      "name": "json5",
      "severity": "high",
      "isDirect": false,
      "via": [
        {
          "source": 1091147,
          "name": "json5",
          "dependency": "json5",
          "title": "Prototype Pollution in JSON5 via Parse Method",
          "url": "https://github.com/advisories/GHSA-9c47-m6qq-7p4h",
          "severity": "high",
          "cwe": [
            "CWE-1321"
          ],
          "cvss": {
            "score": 7.1,
            "vectorString": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:L/A:H"
          },
          "range": "<1.0.2"
        }
      ],
      "effects": [
        "babel-core"
      ],
      "range": "<1.0.2",
      "nodes": [
        "node_modules/json5"
      ],
      "fixAvailable": {
        "name": "babel-core",
        "version": "4.7.16",
        "isSemVerMajor": true
      }
    },
    "minimatch": {
      "name": "minimatch",
      "severity": "high",
      "isDirect": false,
      "via": [
        {
          "source": 1091174,
          "name": "minimatch",
          "dependency": "minimatch",
          "title": "minimatch ReDoS vulnerability",
          "url": "https://github.com/advisories/GHSA-f8q6-p94x-37v3",
          "severity": "high",
          "cwe": [
            "CWE-400"
          ],
          "cvss": {
            "score": 7.5,
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
          },
          "range": "<3.0.5"
        }
      ],
      "effects": [],
      "range": "<3.0.5",
      "nodes": [
        "node_modules/minimatch"
      ],
      "fixAvailable": true
    },
    "minimist": {
      "name": "minimist",
      "severity": "critical",
      "isDirect": false,
      "via": [
        {
          "source": 1091173,
          "name": "minimist",
          "dependency": "minimist",
          "title": "Prototype Pollution in minimist",
          "url": "https://github.com/advisories/GHSA-xvch-5gv4-984h",
          "severity": "critical",
          "cwe": [
            "CWE-1321"
          ],
          "cvss": {
            "score": 9.8,
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
          },
          "range": ">=1.0.0 <1.2.6"
        }
      ],
      "effects": [],
      "range": "1.0.0 - 1.2.5",
      "nodes": [
        "node_modules/minimist"
      ],
      "fixAvailable": true
    }
  },
  "metadata": {
    "vulnerabilities": {
      "info": 0,
      "low": 1,
      "moderate": 0,
      "high": 7,
      "critical": 1,
      "total": 9
    },
    "dependencies": {
      "prod": 1,
      "dev": 1018,
      "optional": 2,
      "peer": 0,
      "peerOptional": 0,
      "total": 1018
    }
  }
}

--- end ---
$ /usr/bin/composer install
--- stderr ---
No lock file found. Updating dependencies instead of installing from lock file. Use composer update over composer install if you do not have a lock file.
Loading composer repositories with package information
Info from https://repo.packagist.org: #StandWithUkraine
Updating dependencies
Lock file operations: 36 installs, 0 updates, 0 removals
  - Locking composer/pcre (3.1.0)
  - Locking composer/semver (3.3.2)
  - Locking composer/spdx-licenses (1.5.7)
  - Locking composer/xdebug-handler (3.0.3)
  - Locking doctrine/deprecations (v1.0.0)
  - Locking felixfbecker/advanced-json-rpc (v3.2.1)
  - Locking mediawiki/mediawiki-codesniffer (v41.0.0)
  - Locking mediawiki/mediawiki-phan-config (0.12.0)
  - Locking mediawiki/minus-x (1.1.1)
  - Locking mediawiki/phan-taint-check-plugin (4.0.0)
  - Locking microsoft/tolerant-php-parser (v0.1.1)
  - Locking netresearch/jsonmapper (v4.1.0)
  - Locking phan/phan (5.4.1)
  - Locking php-parallel-lint/php-console-color (v1.0.1)
  - Locking php-parallel-lint/php-console-highlighter (v1.0.0)
  - Locking php-parallel-lint/php-parallel-lint (v1.3.2)
  - Locking phpdocumentor/reflection-common (2.2.0)
  - Locking phpdocumentor/reflection-docblock (5.3.0)
  - Locking phpdocumentor/type-resolver (1.7.0)
  - Locking phpstan/phpdoc-parser (1.16.1)
  - Locking psr/container (1.1.2)
  - Locking psr/log (1.1.4)
  - Locking sabre/event (5.1.4)
  - Locking squizlabs/php_codesniffer (3.7.2)
  - Locking symfony/console (v5.4.21)
  - Locking symfony/deprecation-contracts (v2.5.2)
  - Locking symfony/polyfill-ctype (v1.27.0)
  - Locking symfony/polyfill-intl-grapheme (v1.27.0)
  - Locking symfony/polyfill-intl-normalizer (v1.27.0)
  - Locking symfony/polyfill-mbstring (v1.27.0)
  - Locking symfony/polyfill-php73 (v1.27.0)
  - Locking symfony/polyfill-php80 (v1.27.0)
  - Locking symfony/service-contracts (v2.5.2)
  - Locking symfony/string (v5.4.21)
  - Locking tysonandre/var_representation_polyfill (0.1.3)
  - Locking webmozart/assert (1.11.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 36 installs, 0 updates, 0 removals
    0 [>---------------------------]    0 [->--------------------------]    0 [--->------------------------]  - Installing composer/pcre (3.1.0): Extracting archive
  - Installing symfony/polyfill-php80 (v1.27.0): Extracting archive
  - Installing squizlabs/php_codesniffer (3.7.2): Extracting archive
  - Installing symfony/polyfill-mbstring (v1.27.0): Extracting archive
  - Installing composer/spdx-licenses (1.5.7): Extracting archive
  - Installing composer/semver (3.3.2): Extracting archive
  - Installing mediawiki/mediawiki-codesniffer (v41.0.0): Extracting archive
  - Installing tysonandre/var_representation_polyfill (0.1.3): Extracting archive
  - Installing symfony/polyfill-intl-normalizer (v1.27.0): Extracting archive
  - Installing symfony/polyfill-intl-grapheme (v1.27.0): Extracting archive
  - Installing symfony/polyfill-ctype (v1.27.0): Extracting archive
  - Installing symfony/string (v5.4.21): Extracting archive
  - Installing symfony/deprecation-contracts (v2.5.2): Extracting archive
  - Installing psr/container (1.1.2): Extracting archive
  - Installing symfony/service-contracts (v2.5.2): Extracting archive
  - Installing symfony/polyfill-php73 (v1.27.0): Extracting archive
  - Installing symfony/console (v5.4.21): Extracting archive
  - Installing sabre/event (5.1.4): Extracting archive
  - Installing netresearch/jsonmapper (v4.1.0): Extracting archive
  - Installing microsoft/tolerant-php-parser (v0.1.1): Extracting archive
  - Installing webmozart/assert (1.11.0): Extracting archive
  - Installing phpstan/phpdoc-parser (1.16.1): Extracting archive
  - Installing phpdocumentor/reflection-common (2.2.0): Extracting archive
  - Installing doctrine/deprecations (v1.0.0): Extracting archive
  - Installing phpdocumentor/type-resolver (1.7.0): Extracting archive
  - Installing phpdocumentor/reflection-docblock (5.3.0): Extracting archive
  - Installing felixfbecker/advanced-json-rpc (v3.2.1): Extracting archive
  - Installing psr/log (1.1.4): Extracting archive
  - Installing composer/xdebug-handler (3.0.3): Extracting archive
  - Installing phan/phan (5.4.1): Extracting archive
  - Installing mediawiki/phan-taint-check-plugin (4.0.0): Extracting archive
  - Installing mediawiki/mediawiki-phan-config (0.12.0): Extracting archive
  - Installing mediawiki/minus-x (1.1.1): Extracting archive
  - Installing php-parallel-lint/php-console-color (v1.0.1): Extracting archive
  - Installing php-parallel-lint/php-console-highlighter (v1.0.0): Extracting archive
  - Installing php-parallel-lint/php-parallel-lint (v1.3.2): Extracting archive
  0/27 [>---------------------------]   0%
 10/27 [==========>-----------------]  37%
 20/27 [====================>-------]  74%
 27/27 [============================] 100%4 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating autoload files
14 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
--- stdout ---

--- end ---
Upgrading n:eslint-config-wikimedia from 0.20.0 -> 0.24.0
Upgrading n:grunt from 1.4.0 -> 1.6.1
Upgrading n:grunt-banana-checker from 0.9.0 -> 0.10.0
Upgrading n:grunt-eslint from 23.0.0 -> 24.0.0
Upgrading n:vue from 3.2.31 -> 3.2.37
Upgrading n:@vue/compat from 3.2.31 -> 3.2.37
Upgrading n:@vue/compiler-sfc from 3.2.31 -> 3.2.37
$ /usr/bin/npm install
--- stderr ---
npm WARN deprecated source-map-url@0.4.1: See https://github.com/lydell/source-map-url#deprecated
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated source-map-resolve@0.5.3: See https://github.com/lydell/source-map-resolve#deprecated
npm WARN deprecated sourcemap-codec@1.4.8: Please use @jridgewell/sourcemap-codec instead
npm WARN deprecated core-js@2.6.12: core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.
--- stdout ---

added 1057 packages, and audited 1058 packages in 10s

87 packages are looking for funding
  run `npm fund` for details

7 vulnerabilities (1 low, 5 high, 1 critical)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

--- end ---
$ package-lock-lint package-lock.json
--- stdout ---
Checking package-lock.json

--- end ---
$ /usr/bin/npm install grunt-eslint@24.0.0 --save-exact
--- stdout ---

up to date, audited 1058 packages in 2s

87 packages are looking for funding
  run `npm fund` for details

7 vulnerabilities (1 low, 5 high, 1 critical)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

--- end ---
$ package-lock-lint package-lock.json
--- stdout ---
Checking package-lock.json

--- end ---
$ ./node_modules/.bin/eslint resources/store/index.js i18n/awa.json resources/components/base/Tabs.vue resources/components/base/Message.vue resources/components/FadeIn.vue resources/models/Image.js i18n/et.json resources/components/Spinner.vue i18n/ca.json tests/jest/fixtures/apiResponse.json i18n/nl.json i18n/gu.json i18n/tly.json i18n/api/lt.json i18n/api/zh-hans.json i18n/sms.json package.json i18n/bg.json i18n/pa.json resources/components/ImageExclusionNotice.vue i18n/es-formal.json i18n/api/pl.json i18n/scn.json i18n/af.json resources/plugins/api.js resources/models/Suggestion.js i18n/api/ia.json i18n/cs.json tests/jest/getters.test.js i18n/ml.json i18n/es.json i18n/az.json i18n/sl.json i18n/api/ca.json resources/components/App.vue i18n/api/es.json i18n/zgh.json i18n/api/lb.json i18n/nn.json i18n/mk.json i18n/io.json tests/jest/actions.test.js i18n/blk.json Gruntfile.js jest.setup.js i18n/it.json i18n/tr.json i18n/api/sh.json i18n/api/de.json i18n/diq.json resources/components/SuggestionsGroup.vue i18n/pl.json i18n/ru.json resources/store/utils.js i18n/yue.json resources/components/CategoriesList.vue i18n/api/ko.json i18n/lb.json i18n/api/sl.json i18n/qqq.json i18n/be-tarask.json i18n/ja.json i18n/mni.json extension.json resources/widgets/ConfirmTagsDialogContent.js resources/components/base/Icon.vue i18n/sh.json resources/plugins/logger.js i18n/pt-br.json i18n/bs.json i18n/api/pt-br.json i18n/api/diq.json resources/widgets/base/FormatValueElement.js i18n/arc.json i18n/hyw.json i18n/ksw.json tests/jest/fixtures/imageData.json i18n/api/mk.json i18n/kcg.json i18n/lv.json i18n/th.json i18n/api/zh-hant.json resources/components/ImageCard.vue i18n/lt.json i18n/sr-el.json i18n/en.json resources/store/state.js i18n/de.json resources/components/PersonalUploadsCount.vue i18n/sd.json i18n/api/sv.json i18n/fr.json resources/components/base/Button.vue i18n/api/pt.json i18n/gcr.json i18n/api/he.json i18n/roa-tara.json i18n/br.json __mocks__/wikibase.serialization.js i18n/sr-ec.json i18n/fi.json resources/widgets/base/DOMLessGroupWidget.js i18n/yi.json i18n/api/ru.json i18n/ia.json package-lock.json i18n/hy.json i18n/sv.json i18n/ti.json resources/widgets/base/TemplateRenderingDOMLessGroupWidget.js i18n/hr.json i18n/api/ja.json resources/widgets/ConfirmTagsDialog.js resources/widgets/UserMessage.js i18n/my.json i18n/fa.json i18n/hu.json resources/store/mutations.js i18n/szy.json i18n/api/it.json i18n/api/nl.json i18n/en-gb.json i18n/sk.json sql/tables.json i18n/api/sms.json resources/components/base/ToastNotification.vue i18n/nb.json i18n/api/tr.json resources/config.json i18n/xmf.json i18n/bn.json resources/components/CardStack.vue resources/components/CardStackPlaceholder.vue i18n/api/roa-tara.json i18n/or.json resources/components/base/Tab.vue i18n/ka.json tests/jest/mutations.test.js i18n/da.json i18n/api/uk.json resources/polyfills/Array.prototype.find.js resources/components/UserMessage.vue tests/jest/CardStack.test.js i18n/smn.json resources/components/base/Suggestion.vue resources/store/getters.js i18n/pnb.json i18n/tt-cyrl.json i18n/ro.json i18n/be.json i18n/ko.json i18n/he.json __mocks__/wikibase.datamodel.js i18n/zh-hans.json i18n/sw.json i18n/ms-arab.json composer.json i18n/hi.json i18n/api/en.json resources/widgets/OnboardingDialog.js tests/jest/App.test.js i18n/nqo.json i18n/api/qqq.json resources/widgets/AddCustomTagDialog.js resources/components/base/ToggleSwitch.vue i18n/se.json resources/init.js jest.config.js tests/jest/ImageCard.test.js i18n/zh-hant.json i18n/api/scn.json resources/widgets/EntityAutocompleteInputWidget.js i18n/pt.json i18n/api/ar.json i18n/ar.json i18n/api/fr.json i18n/sje.json resources/store/actions.js i18n/uk.json i18n/skr-arab.json i18n/api/nb.json i18n/tw.json --fix
--- stdout ---

/src/repo/jest.setup.js
    2:1  error  ES2015 block-scoped variables are forbidden  es-x/no-block-scoped-variables
    3:1  error  ES2015 block-scoped variables are forbidden  es-x/no-block-scoped-variables
    3:7  error  ES2015 destructuring is forbidden            es-x/no-destructuring
   34:1  error  ES2015 block-scoped variables are forbidden  es-x/no-block-scoped-variables
   39:1  error  ES2015 block-scoped variables are forbidden  es-x/no-block-scoped-variables
  136:1  error  ES2015 block-scoped variables are forbidden  es-x/no-block-scoped-variables

/src/repo/resources/components/App.vue
    2:2   error    The '<wbmad-fade-in>' component has been used, but not defined                          vue/no-undef-components
    5:5   error    The '<mw-toast-notification>' component has been used, but not defined                  vue/no-undef-components
   19:5   error    The '<wbmad-image-exclusion-notice>' component has been used, but not defined           vue/no-undef-components
   26:5   error    The '<tabs>' component has been used, but not defined                                   vue/no-undef-components
   28:6   error    The '<tab>' component has been used, but not defined                                    vue/no-undef-components
   29:7   error    The '<card-stack>' component has been used, but not defined                             vue/no-undef-components
   33:6   error    The '<tab>' component has been used, but not defined                                    vue/no-undef-components
   34:7   error    The '<personal-uploads-count>' component has been used, but not defined                 vue/no-undef-components
   35:7   error    The '<card-stack>' component has been used, but not defined                             vue/no-undef-components
  101:1   error    The `// @vue/component` directive should be included on the line before module.exports  mediawiki/vue-exports-component-directive
  209:18  warning  URL.hash() is not supported in IE 11                                                    compat/compat
  209:18  warning  URL is not supported in IE 11                                                           compat/compat

/src/repo/resources/components/CardStack.vue
   92:16  error  'images' is not defined                 vue/no-undef-properties
  101:16  error  'fetchPending' is not defined           vue/no-undef-properties
  110:16  error  'fetchError' is not defined             vue/no-undef-properties
  121:16  error  'currentTab' is not defined             vue/no-undef-properties
  121:50  error  'currentImage' is not defined           vue/no-undef-properties
  138:16  error  'userStats' is not defined              vue/no-undef-properties
  148:16  error  'isUserTab' is not defined              vue/no-undef-properties
  148:34  error  'userHasLabeledUploads' is not defined  vue/no-undef-properties
  178:9   error  'updateCurrentTab' is not defined       vue/no-undef-properties
  189:3   error  'imagesInQueue' is not defined          vue/no-undef-properties
  191:10  error  'getImages' is not defined              vue/no-undef-properties

/src/repo/resources/components/CategoriesList.vue
  31:16  error  'categories' is not defined    vue/no-undef-properties
  38:16  error  'currentImage' is not defined  vue/no-undef-properties

/src/repo/resources/components/ImageCard.vue
  133:16  error  'currentImageTitle' is not defined        vue/no-undef-properties
  140:16  error  'currentImage' is not defined             vue/no-undef-properties
  154:16  error  'categories' is not defined               vue/no-undef-properties
  170:14  error  'confirmedSuggestions' is not defined     vue/no-undef-properties
  170:54  error  'publishPending' is not defined           vue/no-undef-properties
  190:16  error  'currentImageSuggestions' is not defined  vue/no-undef-properties
  227:9   error  'confirmTagsDialog' is not defined        vue/no-undef-properties
  231:18  error  'thumbUrl' is not defined                 vue/no-undef-properties
  232:20  error  'imgTitle' is not defined                 vue/no-undef-properties
  241:9   error  'windowManager' is not defined            vue/no-undef-properties
  255:9   error  'publishTags' is not defined              vue/no-undef-properties
  263:9   error  'skipImage' is not defined                vue/no-undef-properties
  272:9   error  'addCustomTagDialog' is not defined       vue/no-undef-properties
  272:44  error  'wikidataIds' is not defined              vue/no-undef-properties

/src/repo/resources/components/Spinner.vue
  1:1  error  Component name "Spinner" should always be multi-word  vue/multi-word-component-names

/src/repo/resources/components/SuggestionsGroup.vue
  75:47  error  'tagDetailsExpanded' is not defined  vue/no-undef-properties

/src/repo/resources/components/base/Button.vue
    1:1  error    Component name "Button" should always be multi-word    vue/multi-word-component-names
  219:1  warning  This line has a length of 106. Maximum allowed is 100  max-len

/src/repo/resources/components/base/Icon.vue
  1:1  error  Component name "Icon" should always be multi-word  vue/multi-word-component-names

/src/repo/resources/components/base/Message.vue
  1:1  error  Component name "Message" should always be multi-word  vue/multi-word-component-names

/src/repo/resources/components/base/Suggestion.vue
  1:1  error  Component name "Suggestion" should always be multi-word  vue/multi-word-component-names

/src/repo/resources/components/base/Tab.vue
   1:1  error  Component name "Tab" should always be multi-word  vue/multi-word-component-names
  28:3  error  'title' of property found, but never used         vue/no-unused-properties
  33:3  error  'disabled' of property found, but never used      vue/no-unused-properties

/src/repo/resources/components/base/Tabs.vue
    1:1   error  Component name "Tabs" should always be multi-word  vue/multi-word-component-names
  111:19  error  ES2015 'Array.prototype.keys' method is forbidden  es-x/no-array-prototype-keys
  119:20  error  ES2015 'Array.prototype.keys' method is forbidden  es-x/no-array-prototype-keys
  126:19  error  ES2015 'Array.prototype.keys' method is forbidden  es-x/no-array-prototype-keys
  151:54  error  ES2015 'Array.prototype.keys' method is forbidden  es-x/no-array-prototype-keys

✖ 60 problems (57 errors, 3 warnings)


--- end ---
$ ./node_modules/.bin/eslint resources/store/index.js i18n/awa.json resources/components/base/Tabs.vue resources/components/base/Message.vue resources/components/FadeIn.vue resources/models/Image.js i18n/et.json resources/components/Spinner.vue i18n/ca.json tests/jest/fixtures/apiResponse.json i18n/nl.json i18n/gu.json i18n/tly.json i18n/api/lt.json i18n/api/zh-hans.json i18n/sms.json package.json i18n/bg.json i18n/pa.json resources/components/ImageExclusionNotice.vue i18n/es-formal.json i18n/api/pl.json i18n/scn.json i18n/af.json resources/plugins/api.js resources/models/Suggestion.js i18n/api/ia.json i18n/cs.json tests/jest/getters.test.js i18n/ml.json i18n/es.json i18n/az.json i18n/sl.json i18n/api/ca.json resources/components/App.vue i18n/api/es.json i18n/zgh.json i18n/api/lb.json i18n/nn.json i18n/mk.json i18n/io.json tests/jest/actions.test.js i18n/blk.json Gruntfile.js jest.setup.js i18n/it.json i18n/tr.json i18n/api/sh.json i18n/api/de.json i18n/diq.json resources/components/SuggestionsGroup.vue i18n/pl.json i18n/ru.json resources/store/utils.js i18n/yue.json resources/components/CategoriesList.vue i18n/api/ko.json i18n/lb.json i18n/api/sl.json i18n/qqq.json i18n/be-tarask.json i18n/ja.json i18n/mni.json extension.json resources/widgets/ConfirmTagsDialogContent.js resources/components/base/Icon.vue i18n/sh.json resources/plugins/logger.js i18n/pt-br.json i18n/bs.json i18n/api/pt-br.json i18n/api/diq.json resources/widgets/base/FormatValueElement.js i18n/arc.json i18n/hyw.json i18n/ksw.json tests/jest/fixtures/imageData.json i18n/api/mk.json i18n/kcg.json i18n/lv.json i18n/th.json i18n/api/zh-hant.json resources/components/ImageCard.vue i18n/lt.json i18n/sr-el.json i18n/en.json resources/store/state.js i18n/de.json resources/components/PersonalUploadsCount.vue i18n/sd.json i18n/api/sv.json i18n/fr.json resources/components/base/Button.vue i18n/api/pt.json i18n/gcr.json i18n/api/he.json i18n/roa-tara.json i18n/br.json __mocks__/wikibase.serialization.js i18n/sr-ec.json i18n/fi.json resources/widgets/base/DOMLessGroupWidget.js i18n/yi.json i18n/api/ru.json i18n/ia.json package-lock.json i18n/hy.json i18n/sv.json i18n/ti.json resources/widgets/base/TemplateRenderingDOMLessGroupWidget.js i18n/hr.json i18n/api/ja.json resources/widgets/ConfirmTagsDialog.js resources/widgets/UserMessage.js i18n/my.json i18n/fa.json i18n/hu.json resources/store/mutations.js i18n/szy.json i18n/api/it.json i18n/api/nl.json i18n/en-gb.json i18n/sk.json sql/tables.json i18n/api/sms.json resources/components/base/ToastNotification.vue i18n/nb.json i18n/api/tr.json resources/config.json i18n/xmf.json i18n/bn.json resources/components/CardStack.vue resources/components/CardStackPlaceholder.vue i18n/api/roa-tara.json i18n/or.json resources/components/base/Tab.vue i18n/ka.json tests/jest/mutations.test.js i18n/da.json i18n/api/uk.json resources/polyfills/Array.prototype.find.js resources/components/UserMessage.vue tests/jest/CardStack.test.js i18n/smn.json resources/components/base/Suggestion.vue resources/store/getters.js i18n/pnb.json i18n/tt-cyrl.json i18n/ro.json i18n/be.json i18n/ko.json i18n/he.json __mocks__/wikibase.datamodel.js i18n/zh-hans.json i18n/sw.json i18n/ms-arab.json composer.json i18n/hi.json i18n/api/en.json resources/widgets/OnboardingDialog.js tests/jest/App.test.js i18n/nqo.json i18n/api/qqq.json resources/widgets/AddCustomTagDialog.js resources/components/base/ToggleSwitch.vue i18n/se.json resources/init.js jest.config.js tests/jest/ImageCard.test.js i18n/zh-hant.json i18n/api/scn.json resources/widgets/EntityAutocompleteInputWidget.js i18n/pt.json i18n/api/ar.json i18n/ar.json i18n/api/fr.json i18n/sje.json resources/store/actions.js i18n/uk.json i18n/skr-arab.json i18n/api/nb.json i18n/tw.json -f json
--- stdout ---
[{"filePath":"/src/repo/Gruntfile.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/__mocks__/wikibase.datamodel.js","messages":[],"suppressedMessages":[{"ruleId":"no-undef","severity":2,"message":"'jest' is not defined.","line":4,"column":13,"nodeType":"Identifier","messageId":"undef","endLine":4,"endColumn":17,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'jest' is not defined.","line":5,"column":9,"nodeType":"Identifier","messageId":"undef","endLine":5,"endColumn":13,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'jest' is not defined.","line":6,"column":21,"nodeType":"Identifier","messageId":"undef","endLine":6,"endColumn":25,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'jest' is not defined.","line":7,"column":12,"nodeType":"Identifier","messageId":"undef","endLine":7,"endColumn":16,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/__mocks__/wikibase.serialization.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/composer.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/extension.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/af.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/ar.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/ca.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/de.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/diq.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/en.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/es.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/fr.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/he.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/ia.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/it.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/ja.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/ko.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/lb.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/lt.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/mk.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/nb.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/nl.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/pl.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/pt-br.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/pt.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/qqq.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/roa-tara.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/ru.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/scn.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/sh.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/sl.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/sms.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/sv.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/tr.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/uk.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/zh-hans.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/zh-hant.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ar.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/arc.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/awa.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/az.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/be-tarask.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/be.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/bg.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/blk.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/bn.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/br.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/bs.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ca.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/cs.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/da.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/de.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/diq.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/en-gb.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/en.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/es-formal.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/es.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/et.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/fa.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/fi.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/fr.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/gcr.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/gu.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/he.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/hi.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/hr.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/hu.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/hy.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/hyw.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ia.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/io.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/it.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ja.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ka.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/kcg.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ko.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ksw.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/lb.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/lt.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/lv.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/mk.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ml.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/mni.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ms-arab.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/my.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/nb.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/nl.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/nn.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/nqo.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/or.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/pa.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/pl.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/pnb.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/pt-br.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/pt.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/qqq.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ro.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/roa-tara.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ru.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/scn.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sd.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/se.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sh.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sje.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sk.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/skr-arab.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sl.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/smn.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sms.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sr-ec.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sr-el.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sv.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sw.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/szy.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/th.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ti.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/tly.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/tr.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/tt-cyrl.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/tw.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/uk.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/xmf.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/yi.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/yue.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/zgh.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/zh-hans.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/zh-hant.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/jest.config.js","messages":[],"suppressedMessages":[{"ruleId":"max-len","severity":1,"message":"This line has a length of 109. Maximum allowed is 100.","line":25,"column":1,"nodeType":"Program","messageId":"max","endLine":25,"endColumn":107,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":27,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":27,"endColumn":28,"fix":{"range":[905,930],"text":"'resources/**/*.(js|vue)'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":31,"column":21,"nodeType":"Literal","messageId":"wrongQuotes","endLine":31,"endColumn":31,"fix":{"range":[1018,1028],"text":"'coverage'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"array-bracket-spacing","severity":2,"message":"A space is required after '['.","line":34,"column":30,"nodeType":"ArrayExpression","messageId":"missingSpaceAfter","endLine":34,"endColumn":31,"fix":{"range":[1133,1133],"text":" "},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-sparse-arrays","severity":2,"message":"Unexpected comma in middle of array.","line":34,"column":30,"nodeType":"ArrayExpression","messageId":"unexpectedSparseArray","endLine":43,"endColumn":3,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":35,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":35,"endColumn":19,"fix":{"range":[1137,1153],"text":"'/node_modules/'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":36,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":36,"endColumn":34,"fix":{"range":[1157,1188],"text":"'resources/components/index.js'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":37,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":37,"endColumn":31,"fix":{"range":[1192,1220],"text":"'resources/plugins/index.js'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":38,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":38,"endColumn":22,"fix":{"range":[1224,1243],"text":"'resources/init.js'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":39,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":39,"endColumn":22,"fix":{"range":[1247,1266],"text":"'resources/vendor/'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":40,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":40,"endColumn":22,"fix":{"range":[1270,1289],"text":"'resources/widgets'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":41,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":41,"endColumn":24,"fix":{"range":[1293,1314],"text":"'resources/polyfills'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":42,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":42,"endColumn":21,"fix":{"range":[1318,1336],"text":"'resources/config'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"max-len","severity":1,"message":"This line has a length of 103. Maximum allowed is 100.","line":65,"column":1,"nodeType":"Program","messageId":"max","endLine":65,"endColumn":101,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"max-len","severity":1,"message":"This line has a length of 102. Maximum allowed is 100.","line":68,"column":1,"nodeType":"Program","messageId":"max","endLine":68,"endColumn":100,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":73,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":73,"endColumn":13,"fix":{"range":[2263,2273],"text":"'vue-jest'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"max-len","severity":1,"message":"This line has a length of 229. Maximum allowed is 100.","line":80,"column":1,"nodeType":"Program","messageId":"max","endLine":80,"endColumn":227,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"max-len","severity":1,"message":"This line has a length of 101. Maximum allowed is 100.","line":83,"column":1,"nodeType":"Program","messageId":"max","endLine":83,"endColumn":99,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":90,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":90,"endColumn":7,"fix":{"range":[2845,2849],"text":"'js'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":91,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":91,"endColumn":9,"fix":{"range":[2853,2859],"text":"'json'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":92,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":92,"endColumn":8,"fix":{"range":[2863,2868],"text":"'vue'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"max-len","severity":1,"message":"This line has a length of 107. Maximum allowed is 100.","line":95,"column":1,"nodeType":"Program","messageId":"max","endLine":95,"endColumn":105,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":97,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":97,"endColumn":10,"fix":{"range":[3002,3009],"text":"'^vue$'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":97,"column":12,"nodeType":"Literal","messageId":"wrongQuotes","endLine":97,"endColumn":25,"fix":{"range":[3011,3024],"text":"'@vue/compat'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"max-len","severity":1,"message":"This line has a length of 124. Maximum allowed is 100.","line":100,"column":1,"nodeType":"Program","messageId":"max","endLine":100,"endColumn":122,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"max-len","severity":1,"message":"This line has a length of 110. Maximum allowed is 100.","line":141,"column":1,"nodeType":"Program","messageId":"max","endLine":141,"endColumn":108,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":143,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":143,"endColumn":20,"fix":{"range":[4359,4376],"text":"'./jest.setup.js'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"max-len","severity":1,"message":"This line has a length of 114. Maximum allowed is 100.","line":146,"column":1,"nodeType":"Program","messageId":"max","endLine":146,"endColumn":112,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":153,"column":3,"nodeType":"Literal","messageId":"wrongQuotes","endLine":153,"endColumn":16,"fix":{"range":[4657,4670],"text":"'.*\\\\.(vue)$'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":153,"column":18,"nodeType":"Literal","messageId":"wrongQuotes","endLine":153,"endColumn":57,"fix":{"range":[4672,4711],"text":"'<rootDir>/node_modules/@vue/vue3-jest'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"quotes","severity":2,"message":"Strings must use singlequote.","line":157,"column":19,"nodeType":"Literal","messageId":"wrongQuotes","endLine":157,"endColumn":26,"fix":{"range":[4790,4797],"text":"'jsdom'"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"comma-dangle","severity":2,"message":"Unexpected trailing comma.","line":157,"column":26,"nodeType":"Property","messageId":"unexpected","endLine":157,"endColumn":27,"fix":{"range":[4790,6608],"text":"\"jsdom\"\n\n\t// Options that will be passed to the testEnvironment\n\t// testEnvironmentOptions: {},\n\n\t// Adds a location field to test results\n\t// testLocationInResults: false,\n\n\t// The glob patterns Jest uses to detect test files\n\t// testMatch: [\n\t//   \"**/__tests__/**/*.[jt]s?(x)\",\n\t//   \"**/?(*.)+(spec|test).[tj]s?(x)\"\n\t// ],\n\n\t// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped\n\t// testPathIgnorePatterns: [\n\t//   \"/node_modules/\"\n\t// ],\n\n\t// The regexp pattern or array of patterns that Jest uses to detect test files\n\t// testRegex: [],\n\n\t// This option allows the use of a custom results processor\n\t// testResultsProcessor: undefined,\n\n\t// This option allows use of a custom test runner\n\t// testRunner: \"jasmine2\",\n\n\t// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href\n\t// testURL: \"http://localhost\",\n\n\t// Setting this value to \"fake\" allows the use of fake timers for functions such as \"setTimeout\"\n\t// timers: \"real\",\n\n\t// A map from regular expressions to paths to transformers\n\t// transform: undefined,\n\n\t// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation\n\t// transformIgnorePatterns: [\n\t//   \"/node_modules/\"\n\t// ],\n\n\t// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them\n\t// unmockedModulePathPatterns: undefined,\n\n\t// Indicates whether each individual test should be reported during the run\n\t// verbose: undefined,\n\n\t// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode\n\t// watchPathIgnorePatterns: [],\n\n\t// Whether to use watchman for file crawling\n\t// watchman: true,\n}"},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"max-len","severity":1,"message":"This line has a length of 108. Maximum allowed is 100.","line":171,"column":1,"nodeType":"Program","messageId":"max","endLine":171,"endColumn":106,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"max-len","severity":1,"message":"This line has a length of 110. Maximum allowed is 100.","line":185,"column":1,"nodeType":"Program","messageId":"max","endLine":185,"endColumn":108,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"max-len","severity":1,"message":"This line has a length of 128. Maximum allowed is 100.","line":194,"column":1,"nodeType":"Program","messageId":"max","endLine":194,"endColumn":126,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"max-len","severity":1,"message":"This line has a length of 145. Maximum allowed is 100.","line":199,"column":1,"nodeType":"Program","messageId":"max","endLine":199,"endColumn":143,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"max-len","severity":1,"message":"This line has a length of 119. Maximum allowed is 100.","line":205,"column":1,"nodeType":"Program","messageId":"max","endLine":205,"endColumn":117,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/jest.setup.js","messages":[{"ruleId":"es-x/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":2,"column":1,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":2,"endColumn":30},{"ruleId":"es-x/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":3,"column":1,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":3,"endColumn":49},{"ruleId":"es-x/no-destructuring","severity":2,"message":"ES2015 destructuring is forbidden.","line":3,"column":7,"nodeType":"ObjectPattern","messageId":"forbidden","endLine":3,"endColumn":17},{"ruleId":"es-x/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":34,"column":1,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":36,"endColumn":3},{"ruleId":"es-x/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":39,"column":1,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":127,"endColumn":3},{"ruleId":"es-x/no-block-scoped-variables","severity":2,"message":"ES2015 block-scoped variables are forbidden.","line":136,"column":1,"nodeType":"VariableDeclaration","messageId":"forbidden","endLine":140,"endColumn":3}],"suppressedMessages":[],"errorCount":6,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/* global jest:false */\nconst Vue = require( 'vue' );\nconst { config } = require( '@vue/test-utils' );\n\n// Mock Vue plugins in test suites\nconfig.global.mocks = {\n\t$logEvent: jest.fn().mockResolvedValue( {} ),\n\t$i18n: function ( str ) {\n\t\treturn {\n\t\t\ttext: function () { return str; },\n\t\t\tparse: function () { return str; }\n\t\t};\n\t}\n};\n\nconfig.global.directives = {\n\t'i18n-html': function ( el, binding ) {\n\t\tel.innerHTML = String( binding.arg ) + ' (' + binding.value + ')';\n\t}\n};\n\nVue.configureCompat( {\n\tMODE: 3\n} );\n\n// Mock API (instances created ggwith new mw.Api() )\nfunction Api() { }\nApi.prototype.get = jest.fn().mockResolvedValue( {} );\nApi.prototype.post = jest.fn().mockResolvedValue( {} );\nApi.prototype.getToken = jest.fn().mockResolvedValue( {} );\nApi.prototype.postWithToken = jest.fn().mockResolvedValue( {} );\n\n// Fake mw.Title instance\nconst fakeTitle = {\n\tgetRelativeText: jest.fn()\n};\n\n// Mock MW object\nconst mw = {\n\tApi: Api,\n\tRegExp: {\n\t\tescape: jest.fn()\n\t},\n\tTitle: {\n\t\tnewFromText: jest.fn().mockReturnValue( fakeTitle ),\n\t\tmakeTitle: jest.fn( fakeTitle )\n\t},\n\tUri: jest.fn(),\n\tconfig: {\n\t\tget: jest.fn()\n\t},\n\tconfirmCloseWindow: jest.fn(),\n\thook: jest.fn().mockReturnValue( {\n\t\tfire: jest.fn()\n\t} ),\n\texperiments: {\n\t\tgetBucket: jest.fn()\n\t},\n\thtml: {\n\t\tescape: function ( str ) {\n\t\t\treturn str.replace( /['\"<>&]/g, function ( char ) {\n\t\t\t\tswitch ( char ) {\n\t\t\t\t\tcase '\\'': return '&#039;';\n\t\t\t\t\tcase '\"': return '&quot;';\n\t\t\t\t\tcase '<': return '&lt;';\n\t\t\t\t\tcase '>': return '&gt;';\n\t\t\t\t\tcase '&': return '&amp;';\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\t},\n\tjqueryMsg: {\n\t\tparser: jest.fn()\n\t},\n\tlanguage: {\n\t\tconvertNumber: jest.fn(),\n\t\tgetData: jest.fn().mockReturnValue( {} )\n\t},\n\tlog: {\n\t\tdeprecate: jest.fn(),\n\t\twarn: jest.fn()\n\t},\n\tmessage: jest.fn().mockReturnValue( {\n\t\ttext: jest.fn(),\n\t\tparse: jest.fn()\n\t} ),\n\tmsg: jest.fn(),\n\tnow: Date.now.bind( Date ),\n\ttemplate: {\n\t\tget: jest.fn().mockReturnValue( {\n\t\t\trender: jest.fn()\n\t\t} )\n\t},\n\tuser: {\n\t\ttokens: {\n\t\t\tget: jest.fn()\n\t\t},\n\t\toptions: {\n\t\t\tget: jest.fn()\n\t\t},\n\t\tisAnon: jest.fn(),\n\t\tgenerateRandomSessionId: function () {\n\t\t\treturn Math.random().toString();\n\t\t},\n\t\tgetId: function () {\n\t\t\treturn Math.round( Math.random() * 1000 );\n\t\t}\n\t},\n\ttrackSubscribe: jest.fn(),\n\ttrack: jest.fn(),\n\tutil: {\n\t\tgetParamValue: jest.fn(),\n\t\tgetUrl: jest.fn()\n\t},\n\tloader: {\n\t\tload: jest.fn(),\n\t\tusing: jest.fn(),\n\t\trequire: jest.fn()\n\t},\n\trequestIdleCallback: jest.fn(),\n\tstorage: {\n\t\tget: jest.fn(),\n\t\tset: jest.fn(),\n\t\tremove: jest.fn()\n\t},\n\tnotify: jest.fn()\n};\n\nglobal.mw = mw;\n\nglobal.$ = require( 'jquery' );\nglobal.OO = require( 'oojs' );\nrequire( 'oojs-ui' );\nrequire( 'oojs-ui/dist/oojs-ui-wikimediaui.js' );\n\nconst mockWbApi = {\n\tget: jest.fn().mockResolvedValue( {} ),\n\tpost: jest.fn().mockResolvedValue( {} ),\n\tpostWithToken: jest.fn().mockResolvedValue( {} )\n};\n\nglobal.wikibase = {\n\tapi: {\n\t\tgetLocationAgnosticMwApi: function () {\n\t\t\treturn mockWbApi;\n\t\t}\n\t},\n\tutilities: {\n\t\tClaimGuidGenerator: function () {\n\t\t\treturn {\n\t\t\t\tnewGuid: jest.fn()\n\t\t\t};\n\t\t}\n\t}\n};\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]}]},{"filePath":"/src/repo/package-lock.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/package.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/App.vue","messages":[{"ruleId":"vue/no-undef-components","severity":2,"message":"The '<wbmad-fade-in>' component has been used, but not defined.","line":2,"column":2,"nodeType":"VStartTag","messageId":"undef","endLine":2,"endColumn":17},{"ruleId":"vue/no-undef-components","severity":2,"message":"The '<mw-toast-notification>' component has been used, but not defined.","line":5,"column":5,"nodeType":"VStartTag","messageId":"undef","endLine":11,"endColumn":6},{"ruleId":"vue/no-undef-components","severity":2,"message":"The '<wbmad-image-exclusion-notice>' component has been used, but not defined.","line":19,"column":5,"nodeType":"VStartTag","messageId":"undef","endLine":19,"endColumn":35},{"ruleId":"vue/no-undef-components","severity":2,"message":"The '<tabs>' component has been used, but not defined.","line":26,"column":5,"nodeType":"VStartTag","messageId":"undef","endLine":26,"endColumn":58},{"ruleId":"vue/no-undef-components","severity":2,"message":"The '<tab>' component has been used, but not defined.","line":28,"column":6,"nodeType":"VStartTag","messageId":"undef","endLine":28,"endColumn":51},{"ruleId":"vue/no-undef-components","severity":2,"message":"The '<card-stack>' component has been used, but not defined.","line":29,"column":7,"nodeType":"VStartTag","messageId":"undef","endLine":29,"endColumn":35},{"ruleId":"vue/no-undef-components","severity":2,"message":"The '<tab>' component has been used, but not defined.","line":33,"column":6,"nodeType":"VStartTag","messageId":"undef","endLine":33,"endColumn":45},{"ruleId":"vue/no-undef-components","severity":2,"message":"The '<personal-uploads-count>' component has been used, but not defined.","line":34,"column":7,"nodeType":"VStartTag","messageId":"undef","endLine":34,"endColumn":31},{"ruleId":"vue/no-undef-components","severity":2,"message":"The '<card-stack>' component has been used, but not defined.","line":35,"column":7,"nodeType":"VStartTag","messageId":"undef","endLine":35,"endColumn":32},{"ruleId":"mediawiki/vue-exports-component-directive","severity":2,"message":"The `// @vue/component` directive should be included on the line before module.exports","line":101,"column":1,"nodeType":"AssignmentExpression","messageId":"missing-directive","endLine":301,"endColumn":2},{"ruleId":"compat/compat","severity":1,"message":"URL.hash() is not supported in IE 11","line":209,"column":18,"nodeType":"MemberExpression","endLine":209,"endColumn":42},{"ruleId":"compat/compat","severity":1,"message":"URL is not supported in IE 11","line":209,"column":18,"nodeType":"NewExpression","endLine":209,"endColumn":37}],"suppressedMessages":[],"errorCount":10,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<wbmad-fade-in>\n\t\t<div class=\"wbmad-suggested-tags-page\">\n\t\t\t<template v-if=\"showToasts\">\n\t\t\t\t<mw-toast-notification\n\t\t\t\t\tv-for=\"message in imageMessages\"\n\t\t\t\t\t:key=\"message.key\"\n\t\t\t\t\t:type=\"message.type\"\n\t\t\t\t\t:duration=\"message.duration\"\n\t\t\t\t\t@leave=\"onToastLeave\"\n\t\t\t\t>\n\t\t\t\t\t<!-- eslint-disable-next-line mediawiki/no-vue-dynamic-i18n -->\n\t\t\t\t\t<p>{{ $i18n( message.messageKey ) }}</p>\n\t\t\t\t</mw-toast-notification>\n\t\t\t</template>\n\n\t\t\t<!-- Tabs container -->\n\t\t\t<template v-if=\"showTabs\">\n\t\t\t\t<wbmad-image-exclusion-notice></wbmad-image-exclusion-notice>\n\n\t\t\t\t<h2\n\t\t\t\t\tv-i18n-html:machinevision-machineaidedtagging-tabs-heading\n\t\t\t\t\tclass=\"wbmad-suggested-tags-page-tabs-heading\">\n\t\t\t\t</h2>\n\n\t\t\t\t<tabs :active=\"currentTab\" @tab-change=\"onTabChange\">\n\t\t\t\t\t<!-- Popular tab -->\n\t\t\t\t\t<tab name=\"popular\" :title=\"popularTabTitle\">\n\t\t\t\t\t\t<card-stack queue=\"popular\"></card-stack>\n\t\t\t\t\t</tab>\n\n\t\t\t\t\t<!-- User tab -->\n\t\t\t\t\t<tab name=\"user\" :title=\"userTabTitle\">\n\t\t\t\t\t\t<personal-uploads-count></personal-uploads-count>\n\t\t\t\t\t\t<card-stack queue=\"user\"></card-stack>\n\t\t\t\t\t</tab>\n\t\t\t\t</tabs>\n\n\t\t\t\t<p\n\t\t\t\t\tv-i18n-html:machinevision-machineaidedtagging-preferences-link\n\t\t\t\t\tclass=\"wbmad-suggested-tags-page-preferences-link\">\n\t\t\t\t</p>\n\n\t\t\t\t<div\n\t\t\t\t\tv-i18n-html:machinevision-machineaidedtagging-license-information\n\t\t\t\t\tclass=\"wbmad-suggested-tags-page-license-info\">\n\t\t\t\t</div>\n\t\t\t</template>\n\n\t\t\t<!-- Login message container -->\n\t\t\t<template v-else-if=\"!isAuthenticated\">\n\t\t\t\t<!-- eslint-disable-next-line vue/no-v-html -->\n\t\t\t\t<p v-html=\"loginMessage\"></p>\n\t\t\t</template>\n\n\t\t\t<template v-else>\n\t\t\t\t<p v-i18n-html:machinevision-autoconfirmed-message></p>\n\t\t\t</template>\n\t\t</div>\n\t</wbmad-fade-in>\n</template>\n\n<script>\nvar mapState = require( 'vuex' ).mapState,\n\tmapGetters = require( 'vuex' ).mapGetters,\n\tmapActions = require( 'vuex' ).mapActions,\n\tTabs = require( './base/Tabs.vue' ),\n\tTab = require( './base/Tab.vue' ),\n\tToastNotification = require( './base/ToastNotification.vue' ),\n\tCardStack = require( './CardStack.vue' ),\n\tPersonalUploadsCount = require( './PersonalUploadsCount.vue' ),\n\tOnboardingDialog = require( '../widgets/OnboardingDialog.js' ),\n\tFadeIn = require( './FadeIn.vue' ),\n\tImageExclusionNotice = require( './ImageExclusionNotice.vue' ),\n\turl = new mw.Uri();\n\n/**\n * App component\n *\n * The top-level component of the MachineVision Vue application. This is where\n * the tabs for different image queues (popular vs user) can be found. It is\n * also where popup \"toast\" notifications will appear based on success or\n * failure of publish actions. The contents of this component will only be\n * displayed to users who are both logged-in and auto-confirmed; otherwise only\n * a static message will be shown and no other functionality will be available.\n * This component also manages an OOUI onboarding dialog for new users.\n *\n * Much of the important application-wide state lives in Vuex. The app\n * component pays attention to image-specific messages so it can display them\n * in toasts, and it updates the current tab state in Vuex as the user moves\n * between tabs. It is also responsible for firing off the getImages action\n * when it mounts.\n *\n * Finally, this component is responsible for some very basic hash-based\n * routing; users who arrive on the page with a URL hash #popular or #user\n * will see the appropriate tab when the UI loads. As the user changes tabs,\n * the hash will be kept in sync with the current tab state thanks to an event\n * listener added in the mounted() hook. This listener is removed in the\n * beforeDestroy() hook.\n */\nmodule.exports = exports = {\n\t/**\n\t * All child components must be declared here before they can be used in\n\t * templates.\n\t */\n\tcomponents: {\n\t\ttabs: Tabs,\n\t\ttab: Tab,\n\t\t'mw-toast-notification': ToastNotification,\n\t\t'card-stack': CardStack,\n\t\t'personal-uploads-count': PersonalUploadsCount,\n\t\t'wbmad-fade-in': FadeIn,\n\t\t'wbmad-image-exclusion-notice': ImageExclusionNotice\n\t},\n\n\t/**\n\t * Computed properties are a good way to get logic out of templates. They\n\t * automatically update as their dependencies change. Vuex helper functions\n\t * like mapState and mapGetters allow a component to treat state and getter\n\t * values like internal computed properties (making it easy to directly\n\t * reference those values in templates as well).\n\t *\n\t * More here: https://vuejs.org/v2/guide/computed.html#Computed-Properties\n\t */\n\tcomputed: $.extend( {}, mapState( [\n\t\t'currentTab',\n\t\t'imageMessages'\n\t] ), mapGetters( [\n\t\t'isAuthenticated',\n\t\t'isAutoconfirmed',\n\t\t'tabs'\n\t] ), {\n\t\t/**\n\t\t * Whether or not to display the full UI\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tshowTabs: function () {\n\t\t\treturn this.isAuthenticated && this.isAutoconfirmed;\n\t\t},\n\n\t\t/**\n\t\t * Due to limitations of the JS parser, we're parsing this message in\n\t\t * PHP and exporting it as config.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tloginMessage: function () {\n\t\t\treturn mw.config.get( 'wgMVSuggestedTagsLoginMessage' );\n\t\t},\n\n\t\t/**\n\t\t * @return {string}\n\t\t */\n\t\tpopularTabTitle: function () {\n\t\t\treturn this.$i18n( 'machinevision-machineaidedtagging-popular-tab' ).text();\n\t\t},\n\n\t\t/**\n\t\t * @return {string}\n\t\t */\n\t\tuserTabTitle: function () {\n\t\t\treturn this.$i18n( 'machinevision-machineaidedtagging-user-tab' ).text();\n\t\t},\n\n\t\t/**\n\t\t * @return {boolean}\n\t\t */\n\t\tshowToasts: function () {\n\t\t\treturn this.imageMessages && this.imageMessages.length > 0;\n\t\t}\n\t} ),\n\n\t/**\n\t * Methods to be mixed into the Vue instance. You can access these methods\n\t * directly on the VM instance, or use them in directive expressions. All\n\t * methods will have their this context automatically bound to the Vue\n\t * instance.\n\t */\n\tmethods: $.extend( {}, mapActions( [\n\t\t'updateCurrentTab',\n\t\t'getImages',\n\t\t'hideImageMessage'\n\t] ), {\n\t\t/**\n\t\t * Watch the tab change events emitted by the <Tabs> component\n\t\t * to ensure that Vuex state is kept in sync\n\t\t *\n\t\t * @param {VueComponent} tab\n\t\t */\n\t\tonTabChange: function ( tab ) {\n\t\t\twindow.history.replaceState( null, null, '#' + tab.name );\n\t\t\tthis.updateCurrentTab( tab.name );\n\t\t},\n\n\t\t/**\n\t\t * Remove a toast notification that has passed its display duration.\n\t\t *\n\t\t * @param {string} toastKey\n\t\t */\n\t\tonToastLeave: function ( toastKey ) {\n\t\t\tthis.hideImageMessage( toastKey );\n\t\t},\n\n\t\t/**\n\t\t * @param {HashChangeEvent} e\n\t\t */\n\t\tonHashChange: function ( e ) {\n\t\t\tvar newHash = new URL( e.newURL ).hash,\n\t\t\t\tnewTabName = newHash.slice( 1 );\n\n\t\t\tif ( this.tabs.indexOf( newTabName ) !== -1 ) {\n\t\t\t\tthis.updateCurrentTab( newTabName );\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Only new users should see the onboarding dialog. All process\n\t\t * dialog/window manager elements are still handled by OOUI (hence the\n\t\t * need to append an element to the DOM – something you don't normally\n\t\t * need to do in Vue.js)\n\t\t */\n\t\tshowOnboardingDialog: function () {\n\t\t\tvar onboardingDialog,\n\t\t\t\tprefKey = 'wbmad-onboarding-dialog-dismissed',\n\t\t\t\twindowManager;\n\n\t\t\t// Don't show if user has dismissed it or if this isn't the user\n\t\t\t// tab. Type coercion is necessary due to limitations of browser\n\t\t\t// localstorage.\n\t\t\tif ( Number( mw.user.options.get( prefKey ) ) === 1 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\twindowManager = new OO.ui.WindowManager();\n\t\t\tonboardingDialog = new OnboardingDialog( { onboardingPrefKey: prefKey } );\n\n\t\t\t$( document.body ).append( windowManager.$element );\n\t\t\twindowManager.addWindows( [ onboardingDialog ] );\n\t\t\twindowManager.openWindow( onboardingDialog );\n\t\t}\n\t} ),\n\n\t/**\n\t * An object where keys are expressions to watch and values are the\n\t * corresponding callbacks. The value can also be a string of a method\n\t * name, or an Object that contains additional options. Typically the\n\t * value will be a function which can take (newValue, oldValue) as\n\t * arguments.\n\t */\n\twatch: {\n\t\t/**\n\t\t * @param {string} newVal name of newly-active tab\n\t\t */\n\t\tcurrentTab: function ( newVal ) {\n\t\t\tif ( this.isAuthenticated && newVal === 'user' ) {\n\t\t\t\tthis.showOnboardingDialog();\n\t\t\t}\n\t\t}\n\t},\n\n\t/**\n\t * Vue.js exposes a number of \"hooks\" that are called at different points\n\t * in a component's lifecycle:\n\t * https://vuejs.org/v2/guide/instance.html#Instance-Lifecycle-Hooks\n\t *\n\t * The mounted hook is called once the component has been mounted into the\n\t * DOM. It is not called in server-side rendering.\n\t *\n\t * Once this component is mounted, it parses the URL to determine which tab\n\t * to show first and fires off API requests to get images in both queues\n\t * (the actual API request code lives in Vuex). Then it sets up an event\n\t * listener to listen for further hash changes.\n\t */\n\tmounted: function () {\n\t\t// If there's a URL fragment and it's one of the tabs, select that tab.\n\t\t// Otherwise, default to \"popular\" add a fragement to the URL.\n\t\tvar urlFragment = url.fragment,\n\t\t\thash = ( urlFragment && this.tabs.indexOf( urlFragment ) !== -1 ) ?\n\t\t\t\turlFragment :\n\t\t\t\tthis.tabs[ 0 ];\n\n\t\tthis.tabs.forEach( function ( tab ) {\n\t\t\tthis.getImages( { queue: tab } );\n\t\t}.bind( this ) );\n\n\t\twindow.history.replaceState( null, null, '#' + hash );\n\t\tthis.updateCurrentTab( hash );\n\n\t\t// Listen for hash changes.\n\t\twindow.addEventListener( 'hashchange', this.onHashChange );\n\t},\n\n\t/**\n\t * Another lifecycle hook. Called right before the component is destroyed.\n\t * This is a good place to remove event listeners.\n\t */\n\tbeforeDestroy: function () {\n\t\twindow.removeEventListener( 'hashchange', this.onHashChange );\n\t}\n};\n</script>\n\n<style lang=\"less\">\n@import 'mediawiki.mixins';\n@import '../style-variables.less';\n\n.wbmad-suggested-tags-page {\n\tmax-width: @wbmad-size-max-width;\n\n\t.wbmad-suggested-tags-page-tabs-heading {\n\t\tborder: 0;\n\t\tfont-family: @font-family-sans;\n\t\tfont-weight: 600;\n\t\tmargin-top: 20px;\n\t}\n\n\t.mw-tabs__content {\n\t\tpadding: 24px 4px 16px;\n\t}\n\n\t.wbmad-suggested-tags-page-license-info {\n\t\tbox-sizing: border-box;\n\t\tbackground-color: @background-color-framed;\n\t\tpadding: 16px;\n\n\t\tp {\n\t\t\tmargin: 0;\n\t\t}\n\t}\n\n\t.wbmad-suggested-tags-page-preferences-link {\n\t\tmargin: 0 0 16px;\n\t}\n}\n</style>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/CardStack.vue","messages":[{"ruleId":"vue/no-undef-properties","severity":2,"message":"'images' is not defined.","line":92,"column":16,"nodeType":"Identifier","messageId":"undef","endLine":92,"endColumn":22},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'fetchPending' is not defined.","line":101,"column":16,"nodeType":"Identifier","messageId":"undef","endLine":101,"endColumn":28},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'fetchError' is not defined.","line":110,"column":16,"nodeType":"Identifier","messageId":"undef","endLine":110,"endColumn":26},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'currentTab' is not defined.","line":121,"column":16,"nodeType":"Identifier","messageId":"undef","endLine":121,"endColumn":26},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'currentImage' is not defined.","line":121,"column":50,"nodeType":"Identifier","messageId":"undef","endLine":121,"endColumn":62},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'userStats' is not defined.","line":138,"column":16,"nodeType":"Identifier","messageId":"undef","endLine":138,"endColumn":25},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'isUserTab' is not defined.","line":148,"column":16,"nodeType":"Identifier","messageId":"undef","endLine":148,"endColumn":25},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'userHasLabeledUploads' is not defined.","line":148,"column":34,"nodeType":"Identifier","messageId":"undef","endLine":148,"endColumn":55},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'updateCurrentTab' is not defined.","line":178,"column":9,"nodeType":"Identifier","messageId":"undef","endLine":178,"endColumn":25},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'imagesInQueue' is not defined.","line":189,"column":3,"nodeType":"Identifier","messageId":"undef","endLine":189,"endColumn":16},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'getImages' is not defined.","line":191,"column":10,"nodeType":"Identifier","messageId":"undef","endLine":191,"endColumn":19}],"suppressedMessages":[],"errorCount":11,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div class=\"wbmad-suggested-tags-cardstack\">\n\t\t<wbmad-cardstack-placeholder v-if=\"isPending\"></wbmad-cardstack-placeholder>\n\n\t\t<wbmad-fade-in v-else-if=\"isError\">\n\t\t\t<mw-message class=\"wbmad-cardstack-message\" type=\"error\">\n\t\t\t\t<p v-i18n-html:machinevision-failure-message></p>\n\t\t\t</mw-message>\n\t\t</wbmad-fade-in>\n\n\t\t<transition\n\t\t\tv-else-if=\"shouldDisplayImage\"\n\t\t\tname=\"wbmad-fade\"\n\t\t\tmode=\"in-out\"\n\t\t\tappear\n\t\t>\n\t\t\t<wbmad-image-card :key=\"currentImageId\"></wbmad-image-card>\n\t\t</transition>\n\n\t\t<wbmad-user-message\n\t\t\tv-else-if=\"showUserCta\"\n\t\t\tclass=\"wbmad-user-cta\"\n\t\t\t:heading=\"$i18n( 'machinevision-cta-heading' )\"\n\t\t\t:text=\"$i18n( 'machinevision-cta-text' )\"\n\t\t\t:cta=\"$i18n( 'machinevision-cta-cta' )\"\n\t\t\t@cta-click=\"goToPopularTab\"\n\t\t></wbmad-user-message>\n\n\t\t<wbmad-user-message\n\t\t\tv-else-if=\"showUserCtaNoLabeledUploads\"\n\t\t\tclass=\"wbmad-user-cta--no-uploads\"\n\t\t\t:heading=\"$i18n( 'machinevision-no-uploads-cta-heading' )\"\n\t\t\t:text=\"$i18n( 'machinevision-no-uploads-cta-text' )\"\n\t\t\t:cta=\"$i18n( 'machinevision-cta-cta' )\"\n\t\t\t@cta-click=\"goToPopularTab\"\n\t\t></wbmad-user-message>\n\n\t\t<wbmad-user-message\n\t\t\tv-else\n\t\t\tclass=\"wbmad-user-cta--generic-no-images\"\n\t\t\t:heading=\"$i18n( 'machinevision-generic-no-images-heading' )\"\n\t\t\t:text=\"$i18n( 'machinevision-generic-no-images-text' )\"\n\t\t></wbmad-user-message>\n\t</div>\n</template>\n\n<script>\nvar mapState = require( 'vuex' ).mapState,\n\tmapGetters = require( 'vuex' ).mapGetters,\n\tmapActions = require( 'vuex' ).mapActions,\n\tCardStackPlaceholder = require( './CardStackPlaceholder.vue' ),\n\tFadeIn = require( './FadeIn.vue' ),\n\tImageCard = require( './ImageCard.vue' ),\n\tUserImage = require( './UserMessage.vue' ),\n\tMessage = require( './base/Message.vue' );\n\n/**\n * Wrapper component for tab content.\n */\n// @vue/component\nmodule.exports = exports = {\n\tname: 'CardStack',\n\n\tcomponents: {\n\t\t'wbmad-cardstack-placeholder': CardStackPlaceholder,\n\t\t'wbmad-fade-in': FadeIn,\n\t\t'wbmad-image-card': ImageCard,\n\t\t'wbmad-user-message': UserImage,\n\t\t'mw-message': Message\n\t},\n\n\tprops: {\n\t\tqueue: {\n\t\t\ttype: String,\n\t\t\trequired: true\n\t\t}\n\t},\n\n\tcomputed: $.extend( {}, mapState( [\n\t\t'currentTab',\n\t\t'fetchPending',\n\t\t'fetchError',\n\t\t'images',\n\t\t'userStats'\n\t] ), mapGetters( [\n\t\t'currentImage'\n\t] ), {\n\t\t/**\n\t\t * @return {Array}\n\t\t */\n\t\timagesInQueue: function () {\n\t\t\treturn this.images[ this.queue ];\n\t\t},\n\n\t\t/**\n\t\t * Pending state is queue-specific\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisPending: function () {\n\t\t\treturn this.fetchPending[ this.queue ];\n\t\t},\n\n\t\t/**\n\t\t * Fetch error state is queue-specific\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisError: function () {\n\t\t\treturn this.fetchError[ this.queue ];\n\t\t},\n\n\t\t/**\n\t\t * Whether to render the ImageCard.\n\t\t * We need a dedicated computed property for this because\n\t\t * ResourceLoader can't handle \"&&\" in the template.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tshouldDisplayImage: function () {\n\t\t\treturn this.currentTab === this.queue && this.currentImage && this.imagesInQueue;\n\t\t},\n\n\t\t/**\n\t\t * @return {boolean}\n\t\t */\n\t\tisUserTab: function () {\n\t\t\treturn this.queue === 'user';\n\t\t},\n\n\t\t/**\n\t\t * Whether or not the user has labeled uploads, which will determine the\n\t\t * message shown to them when they finish tagging personal uploads.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tuserHasLabeledUploads: function () {\n\t\t\treturn this.userStats.total ? this.userStats.total > 0 : false;\n\t\t},\n\n\t\t/**\n\t\t * Whether or not to show a message and CTA based on the user tagging\n\t\t * all of their images.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tshowUserCta: function () {\n\t\t\treturn this.isUserTab && this.userHasLabeledUploads;\n\t\t},\n\n\t\t/**\n\t\t * Whether or not to show a message and CTA based on the user having no\n\t\t * personal uploads, encouraging them to upload some images.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tshowUserCtaNoLabeledUploads: function () {\n\t\t\treturn this.isUserTab && !this.userHasLabeledUploads;\n\t\t},\n\n\t\t/**\n\t\t * We need a unique ID for each image card so the component isn't\n\t\t * reused. Otherwise, transitions won't work.\n\t\t *\n\t\t * @return {number}\n\t\t */\n\t\tcurrentImageId: function () {\n\t\t\treturn this.currentImage.pageid;\n\t\t}\n\t} ),\n\n\tmethods: $.extend( {}, mapActions( [\n\t\t'getImages',\n\t\t'updateCurrentTab'\n\t] ), {\n\t\tgoToPopularTab: function () {\n\t\t\twindow.history.replaceState( null, null, '#popular' );\n\t\t\tthis.updateCurrentTab( 'popular' );\n\t\t}\n\t} ),\n\n\twatch: {\n\t\t/**\n\t\t * If a queue reaches zero images, attempt to fetch more\n\t\t *\n\t\t * @param {Array} oldVal\n\t\t * @param {Array} newVal\n\t\t */\n\t\timagesInQueue: function ( oldVal, newVal ) {\n\t\t\tif ( newVal.length === 0 ) {\n\t\t\t\tthis.getImages( {\n\t\t\t\t\tqueue: this.currentTab\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t}\n};\n</script>\n\n<style lang=\"less\">\n.wbmad-suggested-tags-cardstack {\n\t// To avoid an empty cardstack for an instant, which collapses the container\n\t// and resets scroll height top the top, we're using an in-out transition,\n\t// which will add the new image card before the old one has finished leaving.\n\t// To avoid overlap, we'll hide the old one and ensure that only the latest\n\t// (i.e. last) image card displays.\n\t.wbmad-image-with-suggestions {\n\t\tdisplay: none;\n\n\t\t&:last-child {\n\t\t\tdisplay: block;\n\t\t}\n\t}\n}\n\n.wbmad-user-cta {\n\t.wbmad-user-message-icon {\n\t\tbackground-image: url( ../icons/empty-state-icon.svg );\n\t}\n}\n\n.wbmad-user-cta--no-uploads,\n.wbmad-user-cta--generic-no-images {\n\t.wbmad-user-message-icon {\n\t\tbackground-image: url( ../icons/empty-state-icon-no-uploads.svg );\n\t}\n}\n\n.wbmad-cardstack-message {\n\t// Avoid a major layout jump for items below cardstack.\n\tmargin-bottom: 150px;\n\n\tp {\n\t\tmargin: 0;\n\t}\n}\n\n// Transitions.\n// Fade in new image.\n.wbmad-fade-enter-active,\n.wbmad-fade-appear-active {\n\ttransition: opacity 0.5s;\n}\n\n.wbmad-fade-enter,\n.wbmad-fade-appear {\n\topacity: 0;\n}\n\n// To avoid a jump in the layout for an instant between images, we can't use\n// the out-in mode. Instead, let's hide the old image and remove it from the\n// layout flow while the new one fades in.\n.wbmad-fade-leave-active {\n\topacity: 0;\n\tposition: absolute;\n}\n</style>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/CardStackPlaceholder.vue","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/CategoriesList.vue","messages":[{"ruleId":"vue/no-undef-properties","severity":2,"message":"'categories' is not defined.","line":31,"column":16,"nodeType":"Identifier","messageId":"undef","endLine":31,"endColumn":26},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'currentImage' is not defined.","line":38,"column":16,"nodeType":"Identifier","messageId":"undef","endLine":38,"endColumn":28}],"suppressedMessages":[],"errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div v-if=\"hasCategories\" class=\"wbmad-category-list\">\n\t\t<span\n\t\t\tv-i18n-html:machinevision-categories-label\n\t\t\tclass=\"wbmad-category-list__label\">\n\t\t</span>\n\t\t<span\n\t\t\tv-for=\"( category, index ) in categories\"\n\t\t\t:key=\"category + index\"\n\t\t\tclass=\"wbmad-category-list__item\"\n\t\t>\n\t\t\t{{ category }}\n\t\t</span>\n\t</div>\n</template>\n\n<script>\nvar mapGetters = require( 'vuex' ).mapGetters;\n\n// @vue/component\nmodule.exports = exports = {\n\tname: 'CategoriesList',\n\n\tcomputed: $.extend( {}, mapGetters( [\n\t\t'currentImage'\n\t] ), {\n\t\t/**\n\t\t * @return {boolean}\n\t\t */\n\t\thasCategories: function () {\n\t\t\treturn this.categories.length > 0;\n\t\t},\n\n\t\t/**\n\t\t * @return {Array}\n\t\t */\n\t\tcategories: function () {\n\t\t\treturn this.currentImage.categories;\n\t\t}\n\t} )\n};\n</script>\n\n<style lang=\"less\">\n@import 'mediawiki.mixins';\n@import '../style-variables.less';\n\n.wbmad-category-list {\n\t// TODO: if the FadeIn transition could take in a duration prop we could\n\t// use that and remove this mixin altogether.\n\t.fade-in( 0.2s );\n\tmargin: 4px 0 0;\n\n\tspan {\n\t\tcolor: @color-base--subtle;\n\t\tfont-size: 0.928em;\n\t}\n}\n\n.wbmad-category-list__label {\n\tmargin: 0 0.4em 0 0;\n}\n\n// This isn't _exactly_ ideal, and spacing this pipe-deliminated list would be\n// more exact if we used flexbox. However, we need the text to wrap like a\n// paragraph, so we'll get as close as we can with a border-right and some magic\n// numbers. We'll at least explicitly set the word-spacing to normal to maximize\n// the chance that the spacing looks even.\n.wbmad-category-list__item {\n\tborder-right: solid 1px @color-base--subtle;\n\tmargin: 0 0.4em 0 0;\n\tpadding-right: 0.4em;\n\tword-spacing: normal;\n\n\t&:last-child {\n\t\tborder-right: 0;\n\t\tmargin: 0;\n\t\tpadding: 0;\n\t}\n}\n</style>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/FadeIn.vue","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/ImageCard.vue","messages":[{"ruleId":"vue/no-undef-properties","severity":2,"message":"'currentImageTitle' is not defined.","line":133,"column":16,"nodeType":"Identifier","messageId":"undef","endLine":133,"endColumn":33},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'currentImage' is not defined.","line":140,"column":16,"nodeType":"Identifier","messageId":"undef","endLine":140,"endColumn":28},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'categories' is not defined.","line":154,"column":16,"nodeType":"Identifier","messageId":"undef","endLine":154,"endColumn":26},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'confirmedSuggestions' is not defined.","line":170,"column":14,"nodeType":"Identifier","messageId":"undef","endLine":170,"endColumn":34},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'publishPending' is not defined.","line":170,"column":54,"nodeType":"Identifier","messageId":"undef","endLine":170,"endColumn":68},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'currentImageSuggestions' is not defined.","line":190,"column":16,"nodeType":"Identifier","messageId":"undef","endLine":190,"endColumn":39},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'confirmTagsDialog' is not defined.","line":227,"column":9,"nodeType":"Identifier","messageId":"undef","endLine":227,"endColumn":26},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'thumbUrl' is not defined.","line":231,"column":18,"nodeType":"Identifier","messageId":"undef","endLine":231,"endColumn":26},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'imgTitle' is not defined.","line":232,"column":20,"nodeType":"Identifier","messageId":"undef","endLine":232,"endColumn":28},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'windowManager' is not defined.","line":241,"column":9,"nodeType":"Identifier","messageId":"undef","endLine":241,"endColumn":22},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'publishTags' is not defined.","line":255,"column":9,"nodeType":"Identifier","messageId":"undef","endLine":255,"endColumn":20},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'skipImage' is not defined.","line":263,"column":9,"nodeType":"Identifier","messageId":"undef","endLine":263,"endColumn":18},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'addCustomTagDialog' is not defined.","line":272,"column":9,"nodeType":"Identifier","messageId":"undef","endLine":272,"endColumn":27},{"ruleId":"vue/no-undef-properties","severity":2,"message":"'wikidataIds' is not defined.","line":272,"column":44,"nodeType":"Identifier","messageId":"undef","endLine":272,"endColumn":55}],"suppressedMessages":[{"ruleId":"camelcase","severity":2,"message":"Identifier 'approved_count' is not in camel case.","line":238,"column":5,"nodeType":"Identifier","messageId":"notCamelCase","endLine":238,"endColumn":19,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'approved_count' is not in camel case.","line":253,"column":5,"nodeType":"Identifier","messageId":"notCamelCase","endLine":253,"endColumn":19,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":14,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div class=\"wbmad-image-with-suggestions\">\n\t\t<wbmad-spinner v-if=\"publishPending\"></wbmad-spinner>\n\n\t\t<div\n\t\t\tclass=\"wbmad-image-with-suggestions__container\"\n\t\t\t:class=\"containerClasses\">\n\t\t\t<div class=\"wbmad-image-with-suggestions__image\">\n\t\t\t\t<div class=\"wbmad-image-with-suggestions__image-wrapper\">\n\t\t\t\t\t<a :href=\"descriptionUrl\" target=\"_blank\">\n\t\t\t\t\t\t<img :src=\"thumbUrl\" alt=\"\">\n\t\t\t\t\t</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div class=\"wbmad-image-with-suggestions__content\">\n\t\t\t\t<div class=\"wbmad-image-with-suggestions__header\">\n\t\t\t\t\t<div class=\"wbmad-image-with-suggestions__header__title\">\n\t\t\t\t\t\t<label class=\"wbmad-image-with-suggestions__title-label\">\n\t\t\t\t\t\t\t<a :href=\"descriptionUrl\" target=\"_blank\">\n\t\t\t\t\t\t\t\t{{ title }}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t<wbmad-categories-list></wbmad-categories-list>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div class=\"wbmad-image-with-suggestions__header__toggle\">\n\t\t\t\t\t\t<mw-toggle-switch\n\t\t\t\t\t\t\tname=\"wbmad-toggle-tag-details\"\n\t\t\t\t\t\t\t:label=\"$i18n( 'machinevision-detailed-tags-toggle-label' )\"\n\t\t\t\t\t\t\t:on=\"tagDetailsExpanded\"\n\t\t\t\t\t\t\t@click=\"toggleTagDetails\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t</mw-toggle-switch>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t<wbmad-suggestions-group @custom-tag-button-click=\"launchCustomTagDialog\">\n\t\t\t\t</wbmad-suggestions-group>\n\n\t\t\t\t<div class=\"wbmad-action-buttons\">\n\t\t\t\t\t<mw-button\n\t\t\t\t\t\tclass=\"wbmad-action-buttons__publish\"\n\t\t\t\t\t\t:primary=\"true\"\n\t\t\t\t\t\t:progressive=\"true\"\n\t\t\t\t\t\t:disabled=\"publishDisabled\"\n\t\t\t\t\t\t:aria-label=\"$i18n( 'machinevision-publish-title' )\"\n\t\t\t\t\t\t@click=\"onPublish\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<span v-i18n-html:machinevision-publish></span>\n\t\t\t\t\t</mw-button>\n\t\t\t\t\t<mw-button\n\t\t\t\t\t\tclass=\"wbmad-action-buttons__skip\"\n\t\t\t\t\t\t:frameless=\"true\"\n\t\t\t\t\t\t:disabled=\"skipDisabled\"\n\t\t\t\t\t\t:aria-label=\"$i18n( 'machinevision-skip-title', title ).parse()\"\n\t\t\t\t\t\t@click=\"onSkip\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<span v-i18n-html:machinevision-skip></span>\n\t\t\t\t\t</mw-button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nvar mapActions = require( 'vuex' ).mapActions,\n\tmapGetters = require( 'vuex' ).mapGetters,\n\tmapState = require( 'vuex' ).mapState,\n\tSpinner = require( './Spinner.vue' ),\n\tButton = require( './base/Button.vue' ),\n\tToggleSwitch = require( './base/ToggleSwitch.vue' ),\n\tSuggestionsGroup = require( './SuggestionsGroup.vue' ),\n\tCategoriesList = require( './CategoriesList.vue' ),\n\tConfirmTagsDialog = require( './../widgets/ConfirmTagsDialog.js' ),\n\tAddCustomTagDialog = require( '../widgets/AddCustomTagDialog.js' );\n\n/**\n * ImageCard component\n *\n * Represents a single image in a queue and the various elements needed to add\n * tags to it: publish/skip buttons, image title and categories, and the\n * toggle-able tags themselves, all wrapped in a \"card\" style UI.\n *\n * This component plays a central role in the overall application, and many\n * crucial actions (like publishing, adding custom tags, etc) will be initiated\n * from here. Since publish actions are asynchronous API calls, this component\n * also manages a spinner component to indicate pending state to the user.\n *\n * Like the App component, this component is also responsible for managing some\n * modal dialogs which still rely on OOUI ProcessDialog and WindowManager\n * widgets. One modal dialog handles the process by which custom tags are added;\n * the other is a confirmation dialog that must be approved before the tags can\n * actually be published. The WindowManager is set up in the mounted hook.\n *\n * A note about data-flow. Users will be confirming individual suggestions\n * here, which toggles the \"confirmed\" state of the data in Vuex. Rather than\n * directly mutating some kind of local state in the child Suggestion\n * components (which receive all necessary data as props from ImageCard),\n * Suggestions emit clidk events which are handled here (this dispatches an\n * action which ultimately changes the data in the Vuex store). Passing props\n * down to a child component and manipulating them there is considered an\n * anti-pattern in Vue.js; child components should only communicate with\n * parents via emitting events or using Vuex. See here for more information:\n * https://vuejs.org/v2/style-guide/#Implicit-parent-child-communication-use-with-caution\n */\n\n// @vue/component\nmodule.exports = exports = {\n\tcomponents: {\n\t\t'mw-button': Button,\n\t\t'mw-toggle-switch': ToggleSwitch,\n\t\t'wbmad-spinner': Spinner,\n\t\t'wbmad-suggestions-group': SuggestionsGroup,\n\t\t'wbmad-categories-list': CategoriesList\n\t},\n\n\tcomputed: $.extend( {}, mapState( [\n\t\t'tagDetailsExpanded'\n\t] ), mapGetters( [\n\t\t'currentImage',\n\t\t'currentImageSuggestions',\n\t\t'currentImageTitle'\n\t] ), mapState( [\n\t\t'publishPending'\n\t] ), {\n\t\t/**\n\t\t * @return {string}\n\t\t */\n\t\ttitle: function () {\n\t\t\treturn this.currentImageTitle;\n\t\t},\n\n\t\t/**\n\t\t * @return {string}\n\t\t */\n\t\tthumbUrl: function () {\n\t\t\treturn this.currentImage.thumburl;\n\t\t},\n\n\t\t/**\n\t\t * @return {string}\n\t\t */\n\t\tdescriptionUrl: function () {\n\t\t\treturn this.currentImage.descriptionurl;\n\t\t},\n\n\t\t/**\n\t\t * @return {boolean}\n\t\t */\n\t\thasCategories: function () {\n\t\t\treturn this.categories.length > 0;\n\t\t},\n\n\t\t/**\n\t\t * @return {Array}\n\t\t */\n\t\tcategories: function () {\n\t\t\treturn this.currentImage.categories;\n\t\t},\n\n\t\t/**\n\t\t * Whether or not the publish button should be disabled.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tpublishDisabled: function () {\n\t\t\tif ( this.confirmedSuggestions.length < 1 || this.publishPending ) {\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether or not the skip button should be disabled.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tskipDisabled: function () {\n\t\t\treturn this.publishPending;\n\t\t},\n\n\t\t/**\n\t\t * @return {Array} Array of suggestion objects\n\t\t */\n\t\tconfirmedSuggestions: function () {\n\t\t\treturn this.currentImageSuggestions.filter( function ( suggestion ) {\n\t\t\t\treturn suggestion.confirmed;\n\t\t\t} );\n\t\t},\n\n\t\t/**\n\t\t * Return an array of the wikidata IDs of existing suggestions.\n\t\t *\n\t\t * @return {Object}\n\t\t */\n\t\twikidataIds: function () {\n\t\t\treturn this.currentImageSuggestions.map( function ( suggestion ) {\n\t\t\t\treturn suggestion.wikidataId;\n\t\t\t} );\n\t\t},\n\n\t\t/**\n\t\t * @return {Object}\n\t\t */\n\t\tcontainerClasses: function () {\n\t\t\treturn {\n\t\t\t\t'wbmad-spinner-active': this.publishPending\n\t\t\t};\n\t\t}\n\t} ),\n\n\tmethods: $.extend( {}, mapActions( [\n\t\t'publishTags',\n\t\t'skipImage',\n\t\t'addCustomTag',\n\t\t'toggleTagDetails'\n\t] ), {\n\t\t/**\n\t\t * Launch the confirmation modal (OOUI modal). If confirmed, runs the\n\t\t * onFinalConfirm method.\n\t\t */\n\t\tonPublish: function () {\n\t\t\tthis.confirmTagsDialog = new ConfirmTagsDialog( {\n\t\t\t\ttagsList: this.confirmedSuggestions.map( function ( tag ) {\n\t\t\t\t\treturn tag.text;\n\t\t\t\t} ).join( ', ' ),\n\t\t\t\timgUrl: this.thumbUrl,\n\t\t\t\timgTitle: this.imgTitle\n\t\t\t} ).connect( this, { confirm: 'onFinalConfirm' } );\n\n\t\t\tthis.$logEvent( {\n\t\t\t\taction: 'publish',\n\t\t\t\t// eslint-disable-next-line camelcase\n\t\t\t\tapproved_count: this.confirmedSuggestions.length\n\t\t\t} );\n\n\t\t\tthis.windowManager.addWindows( [ this.confirmTagsDialog ] );\n\t\t\tthis.windowManager.openWindow( this.confirmTagsDialog );\n\t\t},\n\n\t\t/**\n\t\t * Log an event and dispatch publishTags action. Vuex handles the rest\n\t\t * (all the necessary data is already in the store).\n\t\t */\n\t\tonFinalConfirm: function () {\n\t\t\tthis.$logEvent( {\n\t\t\t\taction: 'confirm',\n\t\t\t\t// eslint-disable-next-line camelcase\n\t\t\t\tapproved_count: this.confirmedSuggestions.length\n\t\t\t} );\n\t\t\tthis.publishTags();\n\t\t},\n\n\t\t/**\n\t\t * Skip the image (remove it from the Vuex queue).\n\t\t */\n\t\tonSkip: function () {\n\t\t\tthis.$logEvent( { action: 'skip' } );\n\t\t\tthis.skipImage();\n\t\t},\n\n\t\t/**\n\t\t * Launch the \"add custom tag\" dialog (OOUI modal)\n\t\t */\n\t\tlaunchCustomTagDialog: function () {\n\t\t\t// Set filter on EntityAutocompleteInputWidget to remove existing\n\t\t\t// suggestions from autocomplete results.\n\t\t\tthis.addCustomTagDialog.setFilter( this.wikidataIds );\n\t\t\tthis.windowManager.openWindow( this.addCustomTagDialog );\n\t\t}\n\t} ),\n\n\t/**\n\t * We are still relying on OOUI for modals, so we need to set up the\n\t * WindowManager and dialog widgets when the component mounts.\n\t * Once MediaWiki exposes an appropriate DOM element for vue-based modals\n\t * to target, we can rewrite this functionality in Vue. This is an\n\t * exception to the general rule of \"don't manipulate the DOM directly from\n\t * Vue\".\n\t */\n\tmounted: function () {\n\t\tthis.windowManager = new OO.ui.WindowManager();\n\t\tthis.addCustomTagDialog = new AddCustomTagDialog().connect( this, {\n\t\t\taddCustomTag: 'addCustomTag'\n\t\t} );\n\n\t\t$( document.body ).append( this.windowManager.$element );\n\t\tthis.windowManager.addWindows( [ this.addCustomTagDialog ] );\n\t}\n};\n</script>\n\n<style lang=\"less\">\n@import 'mediawiki.mixins';\n@import '../style-variables.less';\n\n.wbmad-image-with-suggestions {\n\tposition: relative;\n\n\t&__container {\n\t\tbox-shadow: 0 1px 4px rgba( 0, 0, 0, 0.25 );\n\t\tborder-radius: @wbmad-border-radius-image-card;\n\n\t\t&.wbmad-spinner-active {\n\t\t\topacity: 0.5;\n\t\t}\n\t}\n\n\t&__image {\n\t\t.flex-display();\n\t\tbackground-color: @wbmad-background-color-image-card;\n\t\tborder-radius: @wbmad-border-radius-image-card @wbmad-border-radius-image-card 0 0;\n\t\tjustify-content: center;\n\t\t// Ensure image doesn't overflow border radius.\n\t\toverflow: hidden;\n\n\t\t.wbmad-image-with-suggestions__image-wrapper {\n\t\t\tmax-width: 100%;\n\n\t\t\t@media screen and ( min-width: @width-breakpoint-tablet ) {\n\t\t\t\tmax-width: 800px;\n\t\t\t}\n\t\t}\n\n\t\timg {\n\t\t\tdisplay: block;\n\t\t\theight: auto;\n\t\t\tmax-height: 786px;\n\t\t\tmax-width: 100%;\n\t\t\twidth: auto;\n\n\t\t\t@media screen and ( min-width: @width-breakpoint-tablet ) {\n\t\t\t\tmax-height: 600px;\n\t\t\t}\n\t\t}\n\t}\n\n\t&__content {\n\t\tpadding: 24px;\n\t}\n\n\t&__title-label {\n\t\tdisplay: block;\n\t\tfont-size: 1.125em;\n\t\tfont-weight: bold;\n\n\t\ta {\n\t\t\tcolor: @color-base;\n\t\t}\n\t}\n\n\t.wbmad-spinner {\n\t\tbackground-color: rgba( 255, 255, 255, 0.5 );\n\t\tborder-radius: @wbmad-border-radius-image-card;\n\t\theight: 100%;\n\t\tpadding: 0;\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\twidth: 100%;\n\t\tz-index: 1;\n\t}\n}\n\n.wbmad-image-with-suggestions__header {\n\t.flex-display();\n\t.flex-wrap();\n\talign-items: baseline;\n\tjustify-content: space-between;\n\n\t@media screen and ( min-width: @width-breakpoint-tablet ) {\n\t\t.flex-wrap( @wrap: nowrap );\n\t}\n}\n\n.wbmad-image-with-suggestions__header__toggle {\n\tflex-shrink: 0;\n\tmargin: 16px 0 0;\n\n\t@media screen and ( min-width: @width-breakpoint-tablet ) {\n\t\tmargin: 0 0 0 16px;\n\t}\n\n\t.mw-toggle-switch__label {\n\t\tfont-size: 0.928em;\n\t}\n}\n\n.wbmad-action-buttons {\n\t.flex-display();\n\tfont-size: 1.15em;\n\tfont-weight: 600;\n\n\t&__publish {\n\t\tborder-radius: 4px;\n\t\toverflow: hidden;\n\t\tmargin-right: 12px;\n\t\tpadding-left: 16px;\n\t\tpadding-right: 16px;\n\t}\n\n\t&__skip {\n\t\tmargin-left: auto;\n\t}\n}\n</style>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/ImageExclusionNotice.vue","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/PersonalUploadsCount.vue","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/Spinner.vue","messages":[{"ruleId":"vue/multi-word-component-names","severity":2,"message":"Component name \"Spinner\" should always be multi-word.","line":1,"column":1,"nodeType":null,"messageId":"unexpected"}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div class=\"wbmad-spinner\">\n\t\t<div class=\"wbmad-spinner-bounce\"></div>\n\t</div>\n</template>\n\n<script>\n// @vue/component\nmodule.exports = exports = {\n};\n</script>\n\n<style lang=\"less\">\n@import 'mediawiki.mixins';\n@import '../style-variables.less';\n\n.wbmad-spinner {\n\t@keyframes wbmadSpinnerBouncedelay {\n\t\t0%,\n\t\t50%,\n\t\t100% {\n\t\t\ttransform: scale( 0.625 );\n\t\t}\n\n\t\t// Equals 320ms.\n\t\t20% {\n\t\t\topacity: 0.87;\n\t\t\ttransform: scale( 1 );\n\t\t}\n\t}\n\n\t.flex-display();\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 48px 16px;\n\twhite-space: nowrap;\n\n\t& .wbmad-spinner-bounce,\n\t&::before,\n\t&::after {\n\t\tanimation: wbmadSpinnerBouncedelay 1600ms ease-in-out -160ms infinite both;\n\t\tbackground-color: @wbmad-background-color-spinner;\n\t\tborder-radius: 100%;\n\t\tcontent: '';\n\t\tdisplay: block;\n\t\tfloat: left;\n\t\theight: @wbmad-size-spinner;\n\t\twidth: @wbmad-size-spinner;\n\t}\n\n\t&::before {\n\t\tanimation-delay: -330ms;\n\t\tmargin-right: 4px;\n\t}\n\n\t&::after {\n\t\tanimation-delay: 0s;\n\t\tmargin-left: 4px;\n\t}\n}\n</style>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/SuggestionsGroup.vue","messages":[{"ruleId":"vue/no-undef-properties","severity":2,"message":"'tagDetailsExpanded' is not defined.","line":75,"column":47,"nodeType":"Identifier","messageId":"undef","endLine":75,"endColumn":65}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<transition-group\n\t\tname=\"wbmad-suggestion-fade\"\n\t\ttag=\"div\"\n\t\tclass=\"wbmad-suggestions-group\"\n\t\t:class=\"builtInClasses\"\n\t>\n\t\t<mw-suggestion\n\t\t\tv-for=\"suggestion in currentImageSuggestions\"\n\t\t\t:key=\"getSuggestionKey( suggestion.wikidataId )\"\n\t\t\t:text=\"suggestion.text\"\n\t\t\t:confirmed=\"suggestion.confirmed\"\n\t\t\t@click=\"toggleTagConfirmation( suggestion )\"\n\t\t>\n\t\t\t<template v-if=\"tagDetailsExpanded\">\n\t\t\t\t<label class=\"wbmad-suggestion__label\">\n\t\t\t\t\t<span class=\"wbmad-suggestion__label__text\">\n\t\t\t\t\t\t{{ suggestion.text }}\n\t\t\t\t\t</span>\n\t\t\t\t\t<template v-if=\"suggestion.alias\">\n\t\t\t\t\t\t<span class=\"wbmad-suggestion__label__separator\">–</span>\n\t\t\t\t\t\t<span class=\"wbmad-suggestion__label__alias\">\n\t\t\t\t\t\t\t{{ suggestion.alias }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t</label>\n\t\t\t\t<p v-if=\"suggestion.description\" class=\"wbmad-suggestion__description\">\n\t\t\t\t\t{{ suggestion.description }}\n\t\t\t\t</p>\n\t\t\t</template>\n\n\t\t\t<template v-else>\n\t\t\t\t<label class=\"wbmad-suggestion__label\">{{ suggestion.text }}</label>\n\t\t\t</template>\n\t\t</mw-suggestion><!-- The elements cannot have a breaking space due to a bug in code that will be fixed with this upstream PR: https://github.com/vuejs/vue-next/pull/4647 --><div :key=\"buttonKey\" class=\"wbmad-custom-tag-button-wrapper\">\n\t\t\t<mw-button\n\t\t\t\ticon=\"add\"\n\t\t\t\tclass=\"wbmad-custom-tag-button\"\n\t\t\t\t:title=\"$i18n( 'machinevision-add-custom-tag-title' )\"\n\t\t\t\t@click=\"$emit( 'custom-tag-button-click' )\"\n\t\t\t>\n\t\t\t\t{{ $i18n( 'machinevision-add-custom-tag' ) }}\n\t\t\t</mw-button>\n\t\t</div>\n\t</transition-group>\n</template>\n\n<script>\nvar mapActions = require( 'vuex' ).mapActions,\n\tmapGetters = require( 'vuex' ).mapGetters,\n\tmapState = require( 'vuex' ).mapState,\n\tButton = require( './base/Button.vue' ),\n\tSuggestion = require( './base/Suggestion.vue' );\n\n// @vue/component\nmodule.exports = exports = {\n\n\tcomponents: {\n\t\t'mw-button': Button,\n\t\t'mw-suggestion': Suggestion\n\t},\n\n\tcomputed: $.extend( {}, mapState( [\n\t\t'tagDetailsExpanded'\n\t] ), mapGetters( [\n\t\t'currentImageSuggestions'\n\t] ), {\n\t\t/**\n\t\t * Conditional classes.\n\t\t *\n\t\t * @return {Object}\n\t\t */\n\t\tbuiltInClasses: function () {\n\t\t\treturn {\n\t\t\t\t'wbmad-suggestions-group--expanded': this.tagDetailsExpanded\n\t\t\t};\n\t\t},\n\n\t\tbuttonKey: function () {\n\t\t\tvar modifier = this.tagDetailsExpanded ? '--expanded' : '';\n\t\t\treturn 'add-custom-tag-button' + modifier;\n\t\t}\n\t} ),\n\n\tmethods: $.extend( {}, mapActions( [\n\t\t'toggleTagConfirmation'\n\t] ), {\n\t\tgetSuggestionKey: function ( wikidataId ) {\n\t\t\tvar modifier = this.tagDetailsExpanded ? '--expanded' : '';\n\t\t\treturn wikidataId + modifier;\n\t\t}\n\t} )\n};\n</script>\n\n<style lang=\"less\">\n@import 'mediawiki.mixins';\n@import '../style-variables.less';\n\n.wbmad-suggestions-group {\n\t.flex-display();\n\t.flex-wrap( wrap );\n\tmargin: 18px 0;\n\n\tp {\n\t\tmargin: 0;\n\t}\n}\n\n// Suggestion styles when \"detailed tags\" toggle is on. Most browsers will\n// support the column layout, and there's a flex layout fallback.\n.wbmad-suggestions-group--expanded {\n\tjustify-content: space-between;\n\n\t.mw-suggestion,\n\t.wbmad-custom-tag-button-wrapper {\n\t\t.flex( 0, 0, 100% );\n\t\tmax-width: 100%;\n\n\t\t@media screen and ( min-width: @width-breakpoint-tablet ) {\n\t\t\t.flex( 0, 0, 49.4% );\n\t\t}\n\t}\n\n\t.mw-suggestion {\n\t\tmargin: 0 0 12px 0;\n\t\tpadding: 12px 24/14em;\n\t\t// We want long descriptions to wrap, even if it makes the\n\t\t// suggestion taller.\n\t\twhite-space: normal;\n\n\t\t// We need some special styles to keep the absolutely positioned\n\t\t// checkmark icon in line with our increased padding.\n\t\t.mw-icon {\n\t\t\t// This is 0.25em less than the left padding of the transformed\n\t\t\t// suggestion content, which matches the ratio for non-\n\t\t\t// expanded tags.\n\t\t\tright: 29/28em;\n\t\t\ttop: 12px;\n\t\t}\n\t}\n\n\t.wbmad-suggestion__label__text {\n\t\tfont-weight: bold;\n\t}\n}\n\n// Use column layout if possible.\n@supports ( columns: 2 auto ) {\n\t.wbmad-suggestions-group--expanded {\n\t\t@media screen and ( min-width: @width-breakpoint-tablet ) {\n\t\t\tcolumns: 2 auto;\n\t\t\tcolumn-gap: 12px;\n\t\t\tdisplay: block;\n\n\t\t\t.mw-suggestion {\n\t\t\t\t// Needed to prevent column from breaking mid-suggestion.\n\t\t\t\tdisplay: inline-block;\n\t\t\t\twidth: 100%;\n\t\t\t}\n\t\t}\n\t}\n}\n\n.wbmad-suggestion__label__separator {\n\tmargin: 0 0.4em;\n}\n\n.wbmad-custom-tag-button.mw-button {\n\tborder-radius: 18px;\n\tcolor: @color-base;\n\tline-height: 1.6;\n\tmargin: 0 4px 4px 0;\n\tpadding: 4px 1.25em 4px 30/14em;\n\twhite-space: nowrap;\n\n\t&:hover,\n\t&:focus {\n\t\tcolor: @color-base--emphasized;\n\t}\n\n\t&:focus {\n\t\tborder-color: @color-primary--active;\n\t\tbox-shadow: inset 0 0 0 1px @color-primary--active;\n\t\toutline: 0;\n\t}\n\n\t.mw-icon {\n\t\twidth: 1em;\n\t}\n}\n\n// Transitions.\n.wbmad-suggestion-fade-enter {\n\topacity: 0;\n}\n\n// This is necessary to remove the existing suggestion transitions,\n// which interfere with these transitions (by delaying leaving for the duration\n// of those transitions), and to instantly hide the suggestions that are leaving.\n.wbmad-suggestion-fade-leave,\n.wbmad-suggestion-fade-leave-active,\n.wbmad-suggestion-fade-leave-to {\n\ttransition: none;\n\topacity: 0;\n}\n\n.wbmad-suggestion-fade-enter-active {\n\ttransition: opacity 0.5s;\n}\n</style>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/UserMessage.vue","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/base/Button.vue","messages":[{"ruleId":"vue/multi-word-component-names","severity":2,"message":"Component name \"Button\" should always be multi-word.","line":1,"column":1,"nodeType":null,"messageId":"unexpected"},{"ruleId":"max-len","severity":1,"message":"This line has a length of 106. Maximum allowed is 100.","line":219,"column":1,"nodeType":"Program","messageId":"max","endLine":219,"endColumn":92}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<button\n\t\tclass=\"mw-button\"\n\t\t:class=\"builtInClasses\"\n\t\t:disabled=\"disabled\"\n\t\t@click=\"$emit( 'click' )\"\n\t>\n\t\t<icon\n\t\t\tv-if=\"icon\"\n\t\t\t:icon=\"icon\"\n\t\t\t:invert=\"invert\"\n\t\t>\n\t\t</icon>\n\t\t<div class=\"mw-button__content\">\n\t\t\t<slot></slot>\n\t\t</div>\n\t</button>\n</template>\n\n<script>\nvar Icon = require( './Icon.vue' );\n\n/**\n * Button with optional icon.\n *\n * See ImageCard.vue for usage examples.\n */\n// @vue/component\nmodule.exports = exports = {\n\tcomponents: {\n\t\ticon: Icon\n\t},\n\n\tprops: {\n\t\tdisabled: {\n\t\t\ttype: Boolean\n\t\t},\n\n\t\tframeless: {\n\t\t\ttype: Boolean\n\t\t},\n\n\t\ticon: {\n\t\t\ttype: String,\n\t\t\tdefault: null\n\t\t},\n\n\t\t// Set to true to hide text node.\n\t\tinvisibletext: {\n\t\t\ttype: Boolean\n\t\t},\n\n\t\t// In OOUI, flags are passed in as an array (or a string or an object)\n\t\t// and are handled by a separate mixin. Passing them in individually is\n\t\t// a bit more readable and intuitive, plus it makes the code in this\n\t\t// component simpler.\n\t\tprogressive: {\n\t\t\ttype: Boolean\n\t\t},\n\n\t\tdestructive: {\n\t\t\ttype: Boolean\n\t\t},\n\n\t\tprimary: {\n\t\t\ttype: Boolean\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tbuiltInClasses: function () {\n\t\t\treturn {\n\t\t\t\t'mw-button--framed': !this.frameless,\n\t\t\t\t'mw-button--icon': this.icon,\n\t\t\t\t'mw-button--invisible-text': this.invisibletext,\n\t\t\t\t'mw-button--progressive': this.progressive,\n\t\t\t\t'mw-button--destructive': this.destructive,\n\t\t\t\t'mw-button--primary': this.primary\n\t\t\t};\n\t\t},\n\n\t\tinvert: function () {\n\t\t\treturn ( this.primary || this.disabled ) && !this.frameless;\n\t\t}\n\t}\n};\n</script>\n\n<style lang=\"less\">\n@import 'mediawiki.mixins';\n@import '../../../lib/wikimedia-ui-base.less';\n\n.mw-button {\n\ttransition: background-color 100ms, color 100ms, border-color 100ms, box-shadow 100ms;\n\tbackground-color: transparent;\n\tborder: @border-width-base @border-style-base transparent;\n\tborder-radius: 2px;\n\tcolor: @color-base;\n\tcursor: pointer;\n\tfont-size: inherit;\n\tfont-weight: bold;\n\tpadding: 6px;\n\tuser-select: none;\n\n\t&:hover {\n\t\tbackground-color: rgba( 0, 24, 73, 7/255 );\n\t\tcolor: @color-base--emphasized;\n\t}\n\n\t&:focus {\n\t\tborder-color: @color-primary;\n\t\tbox-shadow: @box-shadow-base--focus;\n\t\toutline: 0;\n\t}\n\n\t.mw-icon {\n\t\theight: 100%;\n\t\tleft: 5/14em;\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\ttransition: opacity 100ms;\n\n\t\t/* stylelint-disable-next-line selector-class-pattern */\n\t\t&:not( .oo-ui-icon-invert ) {\n\t\t\topacity: @opacity-icon-base;\n\t\t}\n\t}\n\n\t// Variants.\n\t&--icon {\n\t\tpadding-left: 30/14em;\n\t\tposition: relative;\n\t}\n\n\t&--framed {\n\t\tbackground-color: @background-color-framed;\n\t\tborder-color: @border-color-base;\n\t\tpadding: 6px 12px;\n\n\t\t&:hover {\n\t\t\tbackground-color: @background-color-framed--hover;\n\t\t\tcolor: @color-base--hover;\n\t\t}\n\n\t\t&.mw-button--icon {\n\t\t\tpadding-left: 38/14em;\n\t\t\tposition: relative;\n\t\t}\n\n\t\t/* stylelint-disable-next-line no-descending-specificity */\n\t\t.mw-icon {\n\t\t\tleft: 11/14em;\n\t\t}\n\t}\n\n\t&--progressive {\n\t\tcolor: @color-primary;\n\n\t\t&:hover {\n\t\t\tcolor: @color-primary--hover;\n\t\t}\n\n\t\t&.mw-button--framed {\n\t\t\t&:hover {\n\t\t\t\tborder-color: @color-primary--hover;\n\t\t\t}\n\t\t}\n\t}\n\n\t&--destructive {\n\t\tcolor: @color-destructive;\n\n\t\t&:hover {\n\t\t\tcolor: @color-destructive--hover;\n\t\t}\n\n\t\t&:focus {\n\t\t\tborder-color: @color-destructive;\n\t\t\tbox-shadow: inset 0 0 0 1px @color-destructive;\n\t\t}\n\n\t\t&.mw-button--framed {\n\t\t\t&:hover {\n\t\t\t\tborder-color: @color-destructive--hover;\n\t\t\t}\n\n\t\t\t&:focus {\n\t\t\t\tbox-shadow: inset 0 0 0 1px @color-destructive;\n\t\t\t}\n\t\t}\n\t}\n\n\t&--primary {\n\t\t&.mw-button--framed {\n\t\t\t// Default to progressive.\n\t\t\tbackground-color: @color-primary;\n\t\t\tborder-color: @color-primary;\n\t\t\tcolor: @color-base--inverted;\n\n\t\t\t&:hover {\n\t\t\t\tbackground-color: @color-primary--hover;\n\t\t\t\tborder-color: @color-primary--hover;\n\t\t\t}\n\n\t\t\t&:focus {\n\t\t\t\tbox-shadow: @box-shadow-primary--focus;\n\t\t\t}\n\n\t\t\t&.mw-button--destructive {\n\t\t\t\tbackground-color: @color-destructive;\n\t\t\t\tborder-color: @color-destructive;\n\n\t\t\t\t&:hover {\n\t\t\t\t\tbackground-color: @color-destructive--hover;\n\t\t\t\t\tborder-color: @color-destructive--hover;\n\t\t\t\t}\n\n\t\t\t\t&:focus {\n\t\t\t\t\tbox-shadow: inset 0 0 0 1px @color-destructive, inset 0 0 0 2px @color-base--inverted;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t&:disabled {\n\t\tcolor: @color-base--disabled;\n\t\tcursor: auto;\n\n\t\t&:hover,\n\t\t&:focus {\n\t\t\tbackground-color: @background-color-base;\n\t\t}\n\n\t\t&.mw-button--framed {\n\t\t\tbackground-color: @background-color-filled--disabled;\n\t\t\tborder-color: @border-color-base--disabled;\n\t\t\tcolor: @color-base--inverted;\n\n\t\t\t&:hover,\n\t\t\t&:focus {\n\t\t\t\tbackground-color: @background-color-filled--disabled;\n\t\t\t\tborder-color: @border-color-base--disabled;\n\t\t\t\tbox-shadow: none;\n\t\t\t}\n\t\t}\n\n\t\t&:not( .mw-button--framed ) .mw-icon {\n\t\t\topacity: @opacity-base--disabled;\n\t\t}\n\t}\n\n\t&--invisible-text {\n\t\tpadding-right: 0;\n\n\t\t.mw-button__content {\n\t\t\tborder: 0;\n\t\t\tclip: rect( 1px, 1px, 1px, 1px );\n\t\t\tdisplay: block;\n\t\t\theight: 1px;\n\t\t\tmargin: -1px;\n\t\t\toverflow: hidden;\n\t\t\tpadding: 0;\n\t\t\tposition: absolute;\n\t\t\twidth: 1px;\n\t\t}\n\t}\n}\n</style>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/base/Icon.vue","messages":[{"ruleId":"vue/multi-word-component-names","severity":2,"message":"Component name \"Icon\" should always be multi-word.","line":1,"column":1,"nodeType":null,"messageId":"unexpected"}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<span\n\t\tclass=\"mw-icon\"\n\t\t:class=\"builtInClasses\"\n\t>\n\t\t{{ label }}\n\t</span>\n</template>\n\n<script>\n/**\n * Icon with an optional visually-hidden label.\n *\n * See Button, Message, and Suggestion for usage examples.\n */\n// @vue/component\nmodule.exports = exports = {\n\tprops: {\n\t\ticon: {\n\t\t\ttype: String,\n\t\t\tdefault: null\n\t\t},\n\t\t// Whether or not to invert the icon color.\n\t\tinvert: {\n\t\t\ttype: Boolean\n\t\t},\n\t\tlabel: {\n\t\t\t// String or message object.\n\t\t\ttype: [ String, Object ],\n\t\t\tdefault: null\n\t\t}\n\t},\n\n\tcomputed: {\n\t\ticonClass: function () {\n\t\t\treturn 'oo-ui-icon-' + this.icon;\n\t\t},\n\t\tbuiltInClasses: function () {\n\t\t\t// Use the existing OOUI classes for icon images for now.\n\t\t\tvar classes = { 'oo-ui-image-invert': this.invert };\n\t\t\tclasses[ this.iconClass ] = true;\n\t\t\treturn classes;\n\t\t}\n\t}\n};\n</script>\n\n<style lang=\"less\">\n.mw-icon {\n\tbackground-size: contain;\n\tbackground-position: center center;\n\tbackground-repeat: no-repeat;\n\tdisplay: inline-block;\n\theight: 20/14em;\n\tline-height: 2.5;\n\tmargin: 0;\n\toverflow: hidden;\n\tpadding: 0;\n\tposition: static;\n\ttext-indent: -9999px;\n\tvertical-align: middle;\n\twidth: 20/14em;\n\tuser-select: none;\n}\n</style>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/base/Message.vue","messages":[{"ruleId":"vue/multi-word-component-names","severity":2,"message":"Component name \"Message\" should always be multi-word.","line":1,"column":1,"nodeType":null,"messageId":"unexpected"}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div\n\t\tclass=\"mw-message\"\n\t\t:class=\"builtInClasses\"\n\t\t:aria-live=\"type !== 'error' ? 'polite' : false\"\n\t\t:role=\"type === 'error' ? 'alert' : false \"\n\t>\n\t\t<icon\n\t\t\t:icon=\"icon\"\n\t\t\t:class=\"iconClass\"\n\t\t>\n\t\t</icon>\n\t\t<div class=\"mw-message__content\">\n\t\t\t<slot></slot>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nvar Icon = require( './Icon.vue' ),\n\tICON_MAP = {\n\t\tnotice: 'infoFilled',\n\t\terror: 'error',\n\t\twarning: 'alert',\n\t\tsuccess: 'check'\n\t};\n\n/**\n * User-facing message with icon.\n *\n * See CardStack for usage example.\n */\n// @vue/component\nmodule.exports = exports = {\n\n\tcomponents: {\n\t\ticon: Icon\n\t},\n\n\tprops: {\n\t\t// Should be one of notice, warning, error, or success.\n\t\ttype: {\n\t\t\ttype: String,\n\t\t\tdefault: 'notice'\n\t\t},\n\t\tinline: {\n\t\t\ttype: Boolean\n\t\t}\n\t},\n\n\tcomputed: {\n\t\ttypeClass: function () {\n\t\t\treturn 'mw-message--' + this.type;\n\t\t},\n\t\tbuiltInClasses: function () {\n\t\t\tvar classes = { 'mw-message--block': !this.inline };\n\t\t\tclasses[ this.typeClass ] = true;\n\t\t\treturn classes;\n\t\t},\n\t\ticon: function () {\n\t\t\treturn ICON_MAP[ this.type ];\n\t\t},\n\t\ticonClass: function () {\n\t\t\treturn 'oo-ui-image-' + this.type;\n\t\t}\n\t}\n};\n</script>\n\n<style lang=\"less\">\n@import 'mediawiki.mixins';\n@import '../../../lib/wikimedia-ui-base.less';\n\n.mw-message {\n\tcolor: @color-notice;\n\tfont-weight: bold;\n\tmax-width: 50em;\n\tposition: relative;\n\n\t&--error {\n\t\tcolor: @color-error;\n\t}\n\n\t&--success {\n\t\tcolor: @color-success;\n\t}\n\n\t&--block {\n\t\tcolor: @color-notice;\n\t\tfont-weight: normal;\n\t\tpadding: 16px 24px;\n\n\t\t&.mw-message--notice {\n\t\t\tbackground-color: @background-color-notice--framed;\n\t\t\tborder: @border-notice;\n\t\t}\n\n\t\t&.mw-message--error {\n\t\t\tbackground-color: @background-color-error--framed;\n\t\t\tborder: @border-error;\n\t\t}\n\n\t\t&.mw-message--warning {\n\t\t\tbackground-color: @background-color-warning--framed;\n\t\t\tborder: @border-warning;\n\t\t}\n\n\t\t&.mw-message--success {\n\t\t\tbackground-color: @background-color-success--framed;\n\t\t\tborder: @border-success;\n\t\t}\n\t}\n\n\t.mw-icon {\n\t\tposition: absolute;\n\t}\n}\n\n.mw-message__content {\n\tmargin-left: 2em;\n}\n</style>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/base/Suggestion.vue","messages":[{"ruleId":"vue/multi-word-component-names","severity":2,"message":"Component name \"Suggestion\" should always be multi-word.","line":1,"column":1,"nodeType":null,"messageId":"unexpected"}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div\n\t\tclass=\"mw-suggestion\"\n\t\t:class=\"builtInClasses\"\n\t\trole=\"checkbox\"\n\t\ttabindex=\"0\"\n\t\t:aria-checked=\"confirmed ? 'true' : 'false'\"\n\t\t@click=\"$emit( 'click' )\"\n\t\t@keyup.enter=\"$emit( 'click' )\"\n\t\t@keydown.space.prevent=\"$emit( 'click' )\"\n\t>\n\t\t<div class=\"mw-suggestion__content\">\n\t\t\t<slot v-if=\"hasSlot\"></slot>\n\t\t\t<label v-else class=\"mw-suggestion__label\">\n\t\t\t\t{{ text }}\n\t\t\t</label>\n\t\t</div>\n\t\t<icon\n\t\t\tclass=\"mw-suggestion__icon\"\n\t\t\ticon=\"check\"\n\t\t\t:title=\"iconText\"\n\t\t\t:label=\"iconText\"\n\t\t>\n\t\t</icon>\n\t</div>\n</template>\n\n<script>\nvar Icon = require( './Icon.vue' );\n\n/**\n * Basically a button with an unconfirmed and a confirmed state.\n *\n * Text is required since it's used for the icon title and label. If a slot is\n * present it will be displayed; otherwise the text will be displayed as a\n * label.\n *\n * See ImageCard for usage example.\n */\n// @vue/component\nmodule.exports = exports = {\n\tcomponents: {\n\t\ticon: Icon\n\t},\n\n\tprops: {\n\t\ttext: {\n\t\t\ttype: String,\n\t\t\trequired: true\n\t\t},\n\n\t\tconfirmed: {\n\t\t\ttype: Boolean\n\t\t}\n\t},\n\n\tdata: function () {\n\t\treturn {\n\t\t\ticonText: this.$i18n( 'machinevision-suggestion-confirm-undo-title', this.text ).parse()\n\t\t};\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Conditional classes.\n\t\t *\n\t\t * @return {Object}\n\t\t */\n\t\tbuiltInClasses: function () {\n\t\t\treturn {\n\t\t\t\t'mw-suggestion--confirmed': this.confirmed\n\t\t\t};\n\t\t},\n\n\t\t/**\n\t\t * @return {boolean}\n\t\t */\n\t\thasSlot: function () {\n\t\t\treturn !!this.$slots.default;\n\t\t}\n\t}\n};\n</script>\n\n<style lang=\"less\">\n@import 'mediawiki.mixins';\n@import '../../../lib/wikimedia-ui-base.less';\n\n.mw-suggestion {\n\tbox-sizing: border-box;\n\ttransition: background-color 100ms, color 100ms, border-color 100ms, box-shadow 100ms;\n\tbackground-color: @background-color-framed;\n\tborder: @border-base;\n\tcolor: @color-base;\n\tcursor: pointer;\n\tmargin: 0 4px 4px 0;\n\tpadding: 4px 1.25em;\n\tborder-radius: 18px;\n\twhite-space: nowrap;\n\n\t@media screen and ( min-width: @width-breakpoint-tablet ) {\n\t\tmargin: 0 8px 8px 0;\n\t}\n\n\t&:hover,\n\t&:focus {\n\t\tbackground-color: @background-color-framed--hover;\n\t\tcolor: @color-base--emphasized;\n\t}\n\n\t&:focus {\n\t\tborder-color: @color-primary--active;\n\t\tbox-shadow: inset 0 0 0 1px @color-primary--active;\n\t\toutline: 0;\n\t}\n\n\tlabel {\n\t\tcursor: pointer;\n\t}\n\n\t.mw-suggestion__content {\n\t\ttransition: transform 0.2s;\n\t\tcursor: pointer;\n\t\tdisplay: inline-block;\n\t}\n\n\t// Check icon shown for confirmed suggestions.\n\t.mw-icon {\n\t\ttransition: opacity 0.2s;\n\t\tmin-height: 0;\n\t\tmin-width: 0;\n\t\topacity: 0;\n\t\tposition: absolute;\n\t\tright: 0.5em;\n\t\tvisibility: hidden; // Hide from screen readers.\n\t\twidth: 0; // Don't take up space yet.\n\t}\n\n\t&--confirmed {\n\t\tbackground-color: @background-color-primary;\n\t\tborder-color: @color-primary--active;\n\t\tcolor: @color-base--emphasized;\n\t\tposition: relative;\n\n\t\t&:hover,\n\t\t&:focus {\n\t\t\tbackground-color: @background-color-primary;\n\t\t}\n\n\t\t.mw-suggestion__content {\n\t\t\ttransform: translateX( -0.5em );\n\t\t}\n\n\t\t.mw-icon {\n\t\t\topacity: 1;\n\t\t\tvisibility: visible;\n\t\t\twidth: 1em;\n\t\t}\n\t}\n}\n</style>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/base/Tab.vue","messages":[{"ruleId":"vue/multi-word-component-names","severity":2,"message":"Component name \"Tab\" should always be multi-word.","line":1,"column":1,"nodeType":null,"messageId":"unexpected"},{"ruleId":"vue/no-unused-properties","severity":2,"message":"'title' of property found, but never used.","line":28,"column":3,"nodeType":"Identifier","messageId":"unused","endLine":28,"endColumn":8},{"ruleId":"vue/no-unused-properties","severity":2,"message":"'disabled' of property found, but never used.","line":33,"column":3,"nodeType":"Identifier","messageId":"unused","endLine":33,"endColumn":11}],"suppressedMessages":[],"errorCount":3,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<transition name=\"mw-tab-fade-in\">\n\t\t<div\n\t\t\tv-show=\"isActive\"\n\t\t\t:id=\"id\"\n\t\t\t:aria-hidden=\"!isActive\"\n\t\t\t:aria-labeledby=\"id + '-label'\"\n\t\t\tclass=\"mw-tab\"\n\t\t\trole=\"tabpanel\"\n\t\t>\n\t\t\t<slot></slot>\n\t\t</div>\n\t</transition>\n</template>\n\n<script>\n/**\n * A single tab, to be used inside a Tabs component. See App for usage example.\n */\n// @vue/component\nmodule.exports = exports = {\n\tprops: {\n\t\tname: {\n\t\t\ttype: String,\n\t\t\trequired: true\n\t\t},\n\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\trequired: true\n\t\t},\n\n\t\tdisabled: {\n\t\t\ttype: Boolean\n\t\t}\n\t},\n\n\tdata: function () {\n\t\treturn {\n\t\t\tisActive: false\n\t\t};\n\t},\n\n\tcomputed: {\n\t\tid: function () {\n\t\t\treturn 'mw-tab-' + this.name;\n\t\t}\n\t}\n};\n</script>\n\n<style lang=\"less\">\n// Transitions.\n.mw-tab-fade-in-enter {\n\topacity: 0;\n}\n\n.mw-tab-fade-in-enter-active {\n\ttransition: opacity 0.25s;\n}\n</style>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/base/Tabs.vue","messages":[{"ruleId":"vue/multi-word-component-names","severity":2,"message":"Component name \"Tabs\" should always be multi-word.","line":1,"column":1,"nodeType":null,"messageId":"unexpected"},{"ruleId":"es-x/no-array-prototype-keys","severity":2,"message":"ES2015 'Array.prototype.keys' method is forbidden.","line":111,"column":19,"nodeType":"MemberExpression","messageId":"forbidden","endLine":111,"endColumn":30},{"ruleId":"es-x/no-array-prototype-keys","severity":2,"message":"ES2015 'Array.prototype.keys' method is forbidden.","line":119,"column":20,"nodeType":"MemberExpression","messageId":"forbidden","endLine":119,"endColumn":31},{"ruleId":"es-x/no-array-prototype-keys","severity":2,"message":"ES2015 'Array.prototype.keys' method is forbidden.","line":126,"column":19,"nodeType":"MemberExpression","messageId":"forbidden","endLine":126,"endColumn":30},{"ruleId":"es-x/no-array-prototype-keys","severity":2,"message":"ES2015 'Array.prototype.keys' method is forbidden.","line":151,"column":54,"nodeType":"MemberExpression","messageId":"forbidden","endLine":151,"endColumn":65}],"suppressedMessages":[{"ruleId":"no-jquery/variable-pattern","severity":1,"message":"jQuery collection names must match the variablePattern","line":143,"column":8,"nodeType":"VariableDeclarator","endLine":143,"endColumn":29,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":5,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div class=\"mw-tabs\">\n\t\t<div\n\t\t\tclass=\"mw-tabs__header\"\n\t\t\trole=\"tablist\"\n\t\t\ttabindex=\"0\"\n\t\t\t:aria-activedescendant=\"currentTabId\"\n\t\t\t@keydown.left=\"moveBack\"\n\t\t\t@keydown.up.prevent=\"moveBack\"\n\t\t\t@keydown.right=\"moveForward\"\n\t\t\t@keydown.down.prevent=\"moveForward\"\n\t\t>\n\t\t\t<div\n\t\t\t\tv-for=\"tab in tabs\"\n\t\t\t\t:id=\"tab.id + '-label'\"\n\t\t\t\t:key=\"tab.title\"\n\t\t\t\t:class=\"determineTabLabelClasses( tab )\"\n\t\t\t\t:aria-selected=\"tab.name === currentTabName\"\n\t\t\t\t:aria-controls=\"tab.id\"\n\t\t\t\tclass=\"mw-tabs__header__item\"\n\t\t\t\trole=\"tab\"\n\t\t\t\t@click=\"selectTab( tab.name )\"\n\t\t\t\t@keyup.enter=\"selectTab( tab.name )\"\n\t\t\t>\n\t\t\t\t{{ tab.title }}\n\t\t\t</div>\n\t\t</div>\n\n\t\t<div class=\"mw-tabs__content\">\n\t\t\t<slot></slot>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n/**\n * A group of tabs with a tab menu. See App for usage example.\n *\n * Tab can be changed via user click on a tab menu item or by changing the\n * active prop passed to the Tabs component.\n */\n// @vue/component\nmodule.exports = exports = {\n\tprops: {\n\t\tactive: {\n\t\t\ttype: String,\n\t\t\tdefault: null\n\t\t}\n\t},\n\tcompatConfig: {\n\t\tMODE: 2\n\t},\n\tdata: function () {\n\t\treturn {\n\t\t\ttabs: {},\n\t\t\tcurrentTabName: null\n\t\t};\n\t},\n\n\tcomputed: {\n\t\tcurrentTabId: function () {\n\t\t\treturn this.tabs[ this.currentTabName ] ?\n\t\t\t\tthis.tabs[ this.currentTabName ].id + '-label' :\n\t\t\t\tfalse;\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Change the current tab.\n\t\t *\n\t\t * @param {string} tabName\n\t\t */\n\t\tselectTab: function ( tabName ) {\n\t\t\tif ( this.tabs[ tabName ].disabled === true ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.currentTabName = tabName;\n\t\t},\n\n\t\t/**\n\t\t * Set active attribute on each tab.\n\t\t *\n\t\t * @param {string} currentTabName\n\t\t */\n\t\tsetTabState: function ( currentTabName ) {\n\t\t\tvar tabName;\n\t\t\tfor ( tabName in this.tabs ) {\n\t\t\t\tthis.tabs[ tabName ].isActive = ( tabName === currentTabName );\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Set tab label classes.\n\t\t *\n\t\t * @param {VueComponent} tab\n\t\t * @return {Object}\n\t\t */\n\t\tdetermineTabLabelClasses: function ( tab ) {\n\t\t\treturn {\n\t\t\t\t'is-active': tab.name === this.currentTabName,\n\t\t\t\t'is-disabled': tab.disabled\n\t\t\t};\n\t\t},\n\n\t\t/**\n\t\t * Left or up arrow keydown should move to previous tab, if one exists.\n\t\t */\n\t\tmoveBack: function () {\n\t\t\tvar tabNames = Object.keys( this.tabs ),\n\t\t\t\tpreviousTabIndex = tabNames.indexOf( this.currentTabName ) - 1;\n\n\t\t\tif ( previousTabIndex < 0 ) {\n\t\t\t\t// There is no previous tab, do nothing.\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.selectTab( Object.keys( this.tabs )[ previousTabIndex ] );\n\t\t},\n\n\t\t/**\n\t\t * Right or down arrow keydown should move to next tab, if one exists.\n\t\t */\n\t\tmoveForward: function () {\n\t\t\tvar tabNames = Object.keys( this.tabs ),\n\t\t\t\tnextTabIndex = tabNames.indexOf( this.currentTabName ) + 1;\n\n\t\t\tif ( nextTabIndex >= tabNames.length ) {\n\t\t\t\t// There is no next tab, do nothing.\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.selectTab( tabNames[ nextTabIndex ] );\n\t\t},\n\n\t\t/**\n\t\t * Create an object with tabs keyed by their names, then set the\n\t\t * isActive attribute for each tab.\n\t\t */\n\t\tinitializeTabs: function () {\n\t\t\t// eslint-disable-next-line no-jquery/variable-pattern\n\t\t\tvar tabs = this.$children;\n\t\t\tthis.tabs = {};\n\n\t\t\ttabs.forEach( function ( tab ) {\n\t\t\t\tthis.tabs[ tab.name ] = tab;\n\t\t\t}.bind( this ) );\n\n\t\t\t// If no active tab was passed in as a prop, default to first one.\n\t\t\tthis.currentTabName = this.active ? this.active : Object.keys( this.tabs )[ 0 ];\n\t\t\tthis.setTabState( this.currentTabName );\n\t\t}\n\t},\n\n\twatch: {\n\t\t/**\n\t\t * When the tab stored in state changes, select that tab.\n\t\t *\n\t\t * @param {string} newTabName\n\t\t */\n\t\tactive: function ( newTabName ) {\n\t\t\tthis.selectTab( newTabName );\n\t\t},\n\n\t\t/**\n\t\t * When the current tab changes, set active states and emit an event.\n\t\t *\n\t\t * @param {string} newTabName\n\t\t */\n\t\tcurrentTabName: function () {\n\t\t\tthis.setTabState( this.currentTabName );\n\n\t\t\t// Don't emit an event if the currentTabName changed as a result of\n\t\t\t// the active prop changing. In that case, the parent already knows.\n\t\t\tif ( this.currentTabName !== this.active ) {\n\t\t\t\tthis.$emit( 'tab-change', this.tabs[ this.currentTabName ] );\n\t\t\t}\n\t\t}\n\t},\n\n\tmounted: function () {\n\t\tthis.initializeTabs();\n\t}\n};\n</script>\n\n<style lang=\"less\">\n@import 'mediawiki.mixins';\n@import '../../../lib/wikimedia-ui-base.less';\n\n.mw-tabs {\n\t&__header {\n\t\t.flex-display();\n\t\tbox-shadow: inset 0 -1px 0 0 @border-color-base;\n\n\t\t/* FIXME: Prefix is-active/is-disable classes */\n\t\t/* stylelint-disable selector-class-pattern */\n\t\t&__item {\n\t\t\tcolor: @color-base--subtle;\n\t\t\tcursor: pointer;\n\t\t\tfont-weight: bold;\n\t\t\tmargin: 6px 6px 0 0;\n\t\t\tpadding: 6px 13px;\n\t\t\ttransition: color 100ms, box-shadow 100ms;\n\n\t\t\t&:hover,\n\t\t\t&.is-active {\n\t\t\t\tcolor: @color-primary;\n\t\t\t\tbox-shadow: inset 0 -2px 0 0 @color-primary;\n\t\t\t}\n\n\t\t\t&:hover {\n\t\t\t\tcolor: @color-primary--hover;\n\t\t\t\tbox-shadow: inset 0 -2px 0 0 @color-primary--hover;\n\t\t\t}\n\n\t\t\t&.is-disabled {\n\t\t\t\tcolor: @color-base--disabled;\n\t\t\t\tcursor: not-allowed;\n\n\t\t\t\t&:hover,\n\t\t\t\t&.is-active {\n\t\t\t\t\tcolor: @color-base--disabled;\n\t\t\t\t\tbox-shadow: unset;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t&:focus {\n\t\t\toutline: 0;\n\n\t\t\t.mw-tabs__header__item.is-active {\n\t\t\t\tborder-radius: 2px;\n\t\t\t\tbox-shadow: inset 0 0 0 2px @color-primary;\n\t\t\t}\n\t\t}\n\t\t/* stylelint-enable selector-class-pattern */\n\t}\n\n\t&__content {\n\t\tpadding: 20px;\n\t}\n}\n</style>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/base/ToastNotification.vue","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/components/base/ToggleSwitch.vue","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/config.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/init.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/models/Image.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/models/Suggestion.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/plugins/api.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/plugins/logger.js","messages":[],"suppressedMessages":[{"ruleId":"camelcase","severity":2,"message":"Identifier 'image_title' is not in camel case.","line":28,"column":6,"nodeType":"Identifier","messageId":"notCamelCase","endLine":28,"endColumn":17,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'suggestions_count' is not in camel case.","line":29,"column":6,"nodeType":"Identifier","messageId":"notCamelCase","endLine":29,"endColumn":23,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'is_mobile' is not in camel case.","line":30,"column":6,"nodeType":"Identifier","messageId":"notCamelCase","endLine":30,"endColumn":15,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"camelcase","severity":2,"message":"Identifier 'user_id' is not in camel case.","line":32,"column":6,"nodeType":"Identifier","messageId":"notCamelCase","endLine":32,"endColumn":13,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/polyfills/Array.prototype.find.js","messages":[],"suppressedMessages":[{"ruleId":"no-extend-native","severity":2,"message":"Array prototype is read only, properties should not be added.","line":5,"column":2,"nodeType":"CallExpression","messageId":"unexpected","endLine":47,"endColumn":4,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"eqeqeq","severity":2,"message":"Expected '===' and instead saw '=='.","line":8,"column":14,"nodeType":"BinaryExpression","messageId":"unexpected","endLine":8,"endColumn":16,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"unicorn/throw-new-error","severity":2,"message":"Use `new` when throwing an error.","line":9,"column":11,"nodeType":"CallExpression","messageId":"throw-new-error","endLine":9,"endColumn":55,"fix":{"range":[275,275],"text":"new "},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-bitwise","severity":2,"message":"Unexpected use of '>>>'.","line":15,"column":14,"nodeType":"BinaryExpression","messageId":"unexpected","endLine":15,"endColumn":28,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"unicorn/throw-new-error","severity":2,"message":"Use `new` when throwing an error.","line":19,"column":11,"nodeType":"CallExpression","messageId":"throw-new-error","endLine":19,"endColumn":54,"fix":{"range":[564,564],"text":"new "},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"space-in-parens","severity":2,"message":"There must be a space before this paren.","line":47,"column":3,"nodeType":"Program","messageId":"missingClosingSpace","endLine":47,"endColumn":4,"fix":{"range":[1256,1256],"text":" "},"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"eol-last","severity":2,"message":"Newline required at end of file but not found.","line":48,"column":2,"nodeType":"Program","messageId":"missing","fix":{"range":[1260,1260],"text":"\n"},"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/store/actions.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/store/getters.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/store/index.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/store/mutations.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/store/state.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/store/utils.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/widgets/AddCustomTagDialog.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/widgets/ConfirmTagsDialog.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/widgets/ConfirmTagsDialogContent.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/widgets/EntityAutocompleteInputWidget.js","messages":[],"suppressedMessages":[{"ruleId":"mediawiki/class-doc","severity":2,"message":"All possible CSS classes should be documented. See https://w.wiki/PS2 for details.","line":39,"column":73,"nodeType":"ObjectExpression","endLine":43,"endColumn":3,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/widgets/OnboardingDialog.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/widgets/UserMessage.js","messages":[],"suppressedMessages":[{"ruleId":"mediawiki/class-doc","severity":2,"message":"All possible CSS classes should be documented. See https://w.wiki/PS2 for details.","line":20,"column":2,"nodeType":"CallExpression","endLine":20,"endColumn":73,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/widgets/base/DOMLessGroupWidget.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/widgets/base/FormatValueElement.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/widgets/base/TemplateRenderingDOMLessGroupWidget.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/sql/tables.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/tests/jest/App.test.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/jest/CardStack.test.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/jest/ImageCard.test.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/jest/actions.test.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/jest/fixtures/apiResponse.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/jest/fixtures/imageData.json","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/jest/getters.test.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/jest/mutations.test.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]}]

--- end ---
$ /usr/bin/npm ci --legacy-peer-deps
--- stderr ---
npm WARN deprecated source-map-url@0.4.1: See https://github.com/lydell/source-map-url#deprecated
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated source-map-resolve@0.5.3: See https://github.com/lydell/source-map-resolve#deprecated
npm WARN deprecated sourcemap-codec@1.4.8: Please use @jridgewell/sourcemap-codec instead
npm WARN deprecated core-js@2.6.12: core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.
--- stdout ---

added 1057 packages, and audited 1058 packages in 7s

87 packages are looking for funding
  run `npm fund` for details

7 vulnerabilities (1 low, 5 high, 1 critical)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

--- end ---
$ /usr/bin/npm test
--- stderr ---
PASS tests/jest/mutations.test.js
PASS tests/jest/actions.test.js
PASS tests/jest/getters.test.js
PASS tests/jest/App.test.js
  ● Console

    console.warn
      [Vue warn]: (deprecation OPTIONS_BEFORE_DESTROY) `beforeDestroy` has been renamed to `beforeUnmount`. 
        at <Anonymous ref="VTU_COMPONENT" > 
        at <VTUROOT>

      91 | 		getters.isAutoconfirmed.mockReturnValue( true );
      92 |
    > 93 | 		const wrapper = mount( App, { global: { plugins: [ store ] } } );
         | 		                ^
      94 |
      95 | 		const tabsHeading = wrapper.find( '.wbmad-suggested-tags-page-tabs-heading' );
      96 | 		expect( tabsHeading.exists() ).toBe( true );

      at warn$1 (node_modules/@vue/compat/dist/vue.cjs.js:1759:17)
      at warnDeprecation (node_modules/@vue/compat/dist/vue.cjs.js:2663:5)
      at softAssertCompatEnabled (node_modules/@vue/compat/dist/vue.cjs.js:2749:9)
      at applyOptions (node_modules/@vue/compat/dist/vue.cjs.js:6315:13)
      at finishComponentSetup (node_modules/@vue/compat/dist/vue.cjs.js:10759:9)
      at setupStatefulComponent (node_modules/@vue/compat/dist/vue.cjs.js:10656:9)
      at setupComponent (node_modules/@vue/compat/dist/vue.cjs.js:10578:11)
      at mountComponent (node_modules/@vue/compat/dist/vue.cjs.js:8848:13)
      at processComponent (node_modules/@vue/compat/dist/vue.cjs.js:8819:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8421:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:8965:21)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at setupRenderEffect (node_modules/@vue/compat/dist/vue.cjs.js:9098:9)
      at mountComponent (node_modules/@vue/compat/dist/vue.cjs.js:8865:9)
      at processComponent (node_modules/@vue/compat/dist/vue.cjs.js:8819:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8421:21)
      at render (node_modules/@vue/compat/dist/vue.cjs.js:9604:13)
      at mount (node_modules/@vue/compat/dist/vue.cjs.js:7817:25)
      at Object.app.mount (node_modules/@vue/compat/dist/vue.cjs.js:12983:23)
      at mount (node_modules/@vue/test-utils/dist/vue-test-utils.cjs.js:8002:18)
      at Object.<anonymous> (tests/jest/App.test.js:93:19)

    console.error
      ^ The above deprecation's compat behavior is disabled and will likely lead to runtime errors.

      91 | 		getters.isAutoconfirmed.mockReturnValue( true );
      92 |
    > 93 | 		const wrapper = mount( App, { global: { plugins: [ store ] } } );
         | 		                ^
      94 |
      95 | 		const tabsHeading = wrapper.find( '.wbmad-suggested-tags-page-tabs-heading' );
      96 | 		expect( tabsHeading.exists() ).toBe( true );

      at warnDeprecation (node_modules/@vue/compat/dist/vue.cjs.js:2665:17)
      at softAssertCompatEnabled (node_modules/@vue/compat/dist/vue.cjs.js:2749:9)
      at applyOptions (node_modules/@vue/compat/dist/vue.cjs.js:6315:13)
      at finishComponentSetup (node_modules/@vue/compat/dist/vue.cjs.js:10759:9)
      at setupStatefulComponent (node_modules/@vue/compat/dist/vue.cjs.js:10656:9)
      at setupComponent (node_modules/@vue/compat/dist/vue.cjs.js:10578:11)
      at mountComponent (node_modules/@vue/compat/dist/vue.cjs.js:8848:13)
      at processComponent (node_modules/@vue/compat/dist/vue.cjs.js:8819:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8421:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:8965:21)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at setupRenderEffect (node_modules/@vue/compat/dist/vue.cjs.js:9098:9)
      at mountComponent (node_modules/@vue/compat/dist/vue.cjs.js:8865:9)
      at processComponent (node_modules/@vue/compat/dist/vue.cjs.js:8819:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8421:21)
      at render (node_modules/@vue/compat/dist/vue.cjs.js:9604:13)
      at mount (node_modules/@vue/compat/dist/vue.cjs.js:7817:25)
      at Object.app.mount (node_modules/@vue/compat/dist/vue.cjs.js:12983:23)
      at mount (node_modules/@vue/test-utils/dist/vue-test-utils.cjs.js:8002:18)
      at Object.<anonymous> (tests/jest/App.test.js:93:19)

    console.warn
      [Vue warn]: (deprecation ATTR_FALSE_VALUE) Attribute "role" with v-bind value `false` will render role="false" instead of removing it in Vue 3. To remove the attribute, use `null` or `undefined` instead. If the usage is intended, you can disable the compat behavior and suppress this warning with:
      
        configureCompat({ ATTR_FALSE_VALUE: false })
      
        Details: https://v3-migration.vuejs.org/breaking-changes/attribute-coercion.html 
        at <Anonymous class="wbmad-image-exclusion-notice__message" type="warning" > 
        at <TransitionStub> 
        at <Anonymous> 
        at <TransitionStub> 
        at <WbmadFadeIn> 
        at <Anonymous ref="VTU_COMPONENT" > 
        at <VTUROOT>

      at warn$1 (node_modules/@vue/compat/dist/vue.cjs.js:1759:17)
      at warnDeprecation (node_modules/@vue/compat/dist/vue.cjs.js:2663:5)
      at Object.softAssertCompatEnabled (node_modules/@vue/compat/dist/vue.cjs.js:2749:9)
      at compatCoerceAttr (node_modules/@vue/compat/dist/vue.cjs.js:11549:21)
      at patchAttr (node_modules/@vue/compat/dist/vue.cjs.js:11517:13)
      at patchProp (node_modules/@vue/compat/dist/vue.cjs.js:11769:9)
      at mountElement (node_modules/@vue/compat/dist/vue.cjs.js:8525:25)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8498:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:8965:21)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at setupRenderEffect (node_modules/@vue/compat/dist/vue.cjs.js:9098:9)
      at mountComponent (node_modules/@vue/compat/dist/vue.cjs.js:8865:9)
      at processComponent (node_modules/@vue/compat/dist/vue.cjs.js:8819:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8421:21)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at mountElement (node_modules/@vue/compat/dist/vue.cjs.js:8516:17)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8498:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:8965:21)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at setupRenderEffect (node_modules/@vue/compat/dist/vue.cjs.js:9098:9)
      at mountComponent (node_modules/@vue/compat/dist/vue.cjs.js:8865:9)
      at processComponent (node_modules/@vue/compat/dist/vue.cjs.js:8819:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8421:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:8965:21)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at setupRenderEffect (node_modules/@vue/compat/dist/vue.cjs.js:9098:9)
      at mountComponent (node_modules/@vue/compat/dist/vue.cjs.js:8865:9)
      at processComponent (node_modules/@vue/compat/dist/vue.cjs.js:8819:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8421:21)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at processFragment (node_modules/@vue/compat/dist/vue.cjs.js:8778:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8414:17)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at mountElement (node_modules/@vue/compat/dist/vue.cjs.js:8516:17)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8498:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at processFragment (node_modules/@vue/compat/dist/vue.cjs.js:8778:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8414:17)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at mountElement (node_modules/@vue/compat/dist/vue.cjs.js:8516:17)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8498:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)

    console.error
      ^ The above deprecation's compat behavior is disabled and will likely lead to runtime errors.

      at warnDeprecation (node_modules/@vue/compat/dist/vue.cjs.js:2665:17)
      at Object.softAssertCompatEnabled (node_modules/@vue/compat/dist/vue.cjs.js:2749:9)
      at compatCoerceAttr (node_modules/@vue/compat/dist/vue.cjs.js:11549:21)
      at patchAttr (node_modules/@vue/compat/dist/vue.cjs.js:11517:13)
      at patchProp (node_modules/@vue/compat/dist/vue.cjs.js:11769:9)
      at mountElement (node_modules/@vue/compat/dist/vue.cjs.js:8525:25)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8498:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:8965:21)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at setupRenderEffect (node_modules/@vue/compat/dist/vue.cjs.js:9098:9)
      at mountComponent (node_modules/@vue/compat/dist/vue.cjs.js:8865:9)
      at processComponent (node_modules/@vue/compat/dist/vue.cjs.js:8819:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8421:21)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at mountElement (node_modules/@vue/compat/dist/vue.cjs.js:8516:17)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8498:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:8965:21)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at setupRenderEffect (node_modules/@vue/compat/dist/vue.cjs.js:9098:9)
      at mountComponent (node_modules/@vue/compat/dist/vue.cjs.js:8865:9)
      at processComponent (node_modules/@vue/compat/dist/vue.cjs.js:8819:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8421:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:8965:21)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at setupRenderEffect (node_modules/@vue/compat/dist/vue.cjs.js:9098:9)
      at mountComponent (node_modules/@vue/compat/dist/vue.cjs.js:8865:9)
      at processComponent (node_modules/@vue/compat/dist/vue.cjs.js:8819:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8421:21)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at processFragment (node_modules/@vue/compat/dist/vue.cjs.js:8778:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8414:17)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at mountElement (node_modules/@vue/compat/dist/vue.cjs.js:8516:17)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8498:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at processFragment (node_modules/@vue/compat/dist/vue.cjs.js:8778:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8414:17)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at mountElement (node_modules/@vue/compat/dist/vue.cjs.js:8516:17)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8498:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:8965:21)

    console.warn
      [Vue warn]: (deprecation ATTR_FALSE_VALUE) Attribute "aria-activedescendant" with v-bind value `false` will render aria-activedescendant="false" instead of removing it in Vue 3. To remove the attribute, use `null` or `undefined` instead. If the usage is intended, you can disable the compat behavior and suppress this warning with:
      
        configureCompat({ ATTR_FALSE_VALUE: false })
      
        Details: https://v3-migration.vuejs.org/breaking-changes/attribute-coercion.html 
        at <Anonymous active="popular" onTabChange=fn<bound onTabChange> > 
        at <TransitionStub> 
        at <WbmadFadeIn> 
        at <Anonymous ref="VTU_COMPONENT" > 
        at <VTUROOT>

      at warn$1 (node_modules/@vue/compat/dist/vue.cjs.js:1759:17)
      at warnDeprecation (node_modules/@vue/compat/dist/vue.cjs.js:2663:5)
      at Object.softAssertCompatEnabled (node_modules/@vue/compat/dist/vue.cjs.js:2749:9)
      at compatCoerceAttr (node_modules/@vue/compat/dist/vue.cjs.js:11549:21)
      at patchAttr (node_modules/@vue/compat/dist/vue.cjs.js:11517:13)
      at patchProp (node_modules/@vue/compat/dist/vue.cjs.js:11769:9)
      at mountElement (node_modules/@vue/compat/dist/vue.cjs.js:8525:25)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8498:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at mountElement (node_modules/@vue/compat/dist/vue.cjs.js:8516:17)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8498:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:8965:21)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at setupRenderEffect (node_modules/@vue/compat/dist/vue.cjs.js:9098:9)
      at mountComponent (node_modules/@vue/compat/dist/vue.cjs.js:8865:9)
      at processComponent (node_modules/@vue/compat/dist/vue.cjs.js:8819:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8421:21)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at processFragment (node_modules/@vue/compat/dist/vue.cjs.js:8778:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8414:17)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at mountElement (node_modules/@vue/compat/dist/vue.cjs.js:8516:17)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8498:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at processFragment (node_modules/@vue/compat/dist/vue.cjs.js:8778:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8414:17)
      at mountChildren (node_modules/@vue/compat/dist/vue.cjs.js:8606:13)
      at mountElement (node_modules/@vue/compat/dist/vue.cjs.js:8516:17)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8498:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:8965:21)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at setupRenderEffect (node_modules/@vue/compat/dist/vue.cjs.js:9098:9)
      at mountComponent (node_modules/@vue/compat/dist/vue.cjs.js:8865:9)
      at processComponent (node_modules/@vue/compat/dist/vue.cjs.js:8819:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8421:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:8965:21)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at setupRenderEffect (node_modules/@vue/compat/dist/vue.cjs.js:9098:9)
      at mountComponent (node_modules/@vue/compat/dist/vue.cjs.js:8865:9)
      at processComponent (node_modules/@vue/compat/dist/vue.cjs.js:8819:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8421:21)

    console.warn
      [Vue warn]: (deprecation INSTANCE_CHILDREN) vm.$children has been removed. Consider refactoring your logic to avoid relying on direct access to child components.
        Details: https://v3-migration.vuejs.org/breaking-changes/children.html

      92 | 		},
      93 |
    > 94 | 		/**
         | 		   ^
      95 | 		 * Set tab label classes.
      96 | 		 *
      97 | 		 * @param {VueComponent} tab

      at warn$1 (node_modules/@vue/compat/dist/vue.cjs.js:1759:17)
      at warnDeprecation (node_modules/@vue/compat/dist/vue.cjs.js:2663:5)
      at assertCompatEnabled (node_modules/@vue/compat/dist/vue.cjs.js:2740:9)
      at getCompatChildren (node_modules/@vue/compat/dist/vue.cjs.js:5005:5)
      at Object.$children [as get] (node_modules/@vue/compat/dist/vue.cjs.js:5939:20)
      at Proxy.initializeTabs (resources/components/base/Tabs.vue:94:23)
      at Proxy.mounted (resources/components/base/Tabs.vue:130:10)
      at callWithErrorHandling (node_modules/@vue/compat/dist/vue.cjs.js:1876:22)
      at callWithAsyncErrorHandling (node_modules/@vue/compat/dist/vue.cjs.js:1885:21)
      at Array.hook.__weh.hook.__weh (node_modules/@vue/compat/dist/vue.cjs.js:4964:29)
      at flushPostFlushCbs (node_modules/@vue/compat/dist/vue.cjs.js:2073:47)
      at render (node_modules/@vue/compat/dist/vue.cjs.js:9606:9)
      at mount (node_modules/@vue/compat/dist/vue.cjs.js:7817:25)
      at Object.app.mount (node_modules/@vue/compat/dist/vue.cjs.js:12983:23)
      at mount (node_modules/@vue/test-utils/dist/vue-test-utils.cjs.js:8002:18)
      at Object.<anonymous> (tests/jest/App.test.js:93:19)

    console.warn
      [Vue warn]: (deprecation ATTR_FALSE_VALUE) Attribute "aria-selected" with v-bind value `false` will render aria-selected="false" instead of removing it in Vue 3. To remove the attribute, use `null` or `undefined` instead. If the usage is intended, you can disable the compat behavior and suppress this warning with:
      
        configureCompat({ ATTR_FALSE_VALUE: false })
      
        Details: https://v3-migration.vuejs.org/breaking-changes/attribute-coercion.html 
        at <Anonymous active="popular" onTabChange=fn<bound onTabChange> > 
        at <TransitionStub> 
        at <WbmadFadeIn> 
        at <Anonymous ref="VTU_COMPONENT" > 
        at <VTUROOT>

      at warn$1 (node_modules/@vue/compat/dist/vue.cjs.js:1759:17)
      at warnDeprecation (node_modules/@vue/compat/dist/vue.cjs.js:2663:5)
      at Object.softAssertCompatEnabled (node_modules/@vue/compat/dist/vue.cjs.js:2749:9)
      at compatCoerceAttr (node_modules/@vue/compat/dist/vue.cjs.js:11549:21)
      at patchAttr (node_modules/@vue/compat/dist/vue.cjs.js:11517:13)
      at patchProp (node_modules/@vue/compat/dist/vue.cjs.js:11769:9)
      at mountElement (node_modules/@vue/compat/dist/vue.cjs.js:8525:25)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8498:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at patchKeyedChildren (node_modules/@vue/compat/dist/vue.cjs.js:9240:21)
      at patchChildren (node_modules/@vue/compat/dist/vue.cjs.js:9123:17)
      at processFragment (node_modules/@vue/compat/dist/vue.cjs.js:8808:17)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8414:17)
      at patchBlockChildren (node_modules/@vue/compat/dist/vue.cjs.js:8727:13)
      at patchElement (node_modules/@vue/compat/dist/vue.cjs.js:8635:13)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8501:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:9047:17)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at callWithErrorHandling (node_modules/@vue/compat/dist/vue.cjs.js:1876:36)
      at flushJobs (node_modules/@vue/compat/dist/vue.cjs.js:2110:17)

    console.warn
      [Vue warn]: (deprecation ATTR_FALSE_VALUE) Attribute "aria-hidden" with v-bind value `false` will render aria-hidden="false" instead of removing it in Vue 3. To remove the attribute, use `null` or `undefined` instead. If the usage is intended, you can disable the compat behavior and suppress this warning with:
      
        configureCompat({ ATTR_FALSE_VALUE: false })
      
        Details: https://v3-migration.vuejs.org/breaking-changes/attribute-coercion.html 
        at <TransitionStub> 
        at <Anonymous name="popular" title="machinevision-machineaidedtagging-popular-tab" > 
        at <Anonymous active="popular" onTabChange=fn<bound onTabChange> > 
        at <TransitionStub> 
        at <WbmadFadeIn> 
        at <Anonymous ref="VTU_COMPONENT" > 
        at <VTUROOT>

      at warn$1 (node_modules/@vue/compat/dist/vue.cjs.js:1759:17)
      at warnDeprecation (node_modules/@vue/compat/dist/vue.cjs.js:2663:5)
      at Object.softAssertCompatEnabled (node_modules/@vue/compat/dist/vue.cjs.js:2749:9)
      at compatCoerceAttr (node_modules/@vue/compat/dist/vue.cjs.js:11549:21)
      at patchAttr (node_modules/@vue/compat/dist/vue.cjs.js:11517:13)
      at patchProp (node_modules/@vue/compat/dist/vue.cjs.js:11769:9)
      at patchElement (node_modules/@vue/compat/dist/vue.cjs.js:8681:29)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8501:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at patchKeyedChildren (node_modules/@vue/compat/dist/vue.cjs.js:9204:17)
      at patchChildren (node_modules/@vue/compat/dist/vue.cjs.js:9147:21)
      at patchElement (node_modules/@vue/compat/dist/vue.cjs.js:8642:13)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8501:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:9047:17)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at callWithErrorHandling (node_modules/@vue/compat/dist/vue.cjs.js:1876:36)
      at flushJobs (node_modules/@vue/compat/dist/vue.cjs.js:2110:17)

    console.error
      ^ The above deprecation's compat behavior is disabled and will likely lead to runtime errors.

      at warnDeprecation (node_modules/@vue/compat/dist/vue.cjs.js:2665:17)
      at Object.softAssertCompatEnabled (node_modules/@vue/compat/dist/vue.cjs.js:2749:9)
      at compatCoerceAttr (node_modules/@vue/compat/dist/vue.cjs.js:11549:21)
      at patchAttr (node_modules/@vue/compat/dist/vue.cjs.js:11517:13)
      at patchProp (node_modules/@vue/compat/dist/vue.cjs.js:11769:9)
      at patchElement (node_modules/@vue/compat/dist/vue.cjs.js:8681:29)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8501:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at patchKeyedChildren (node_modules/@vue/compat/dist/vue.cjs.js:9204:17)
      at patchChildren (node_modules/@vue/compat/dist/vue.cjs.js:9147:21)
      at patchElement (node_modules/@vue/compat/dist/vue.cjs.js:8642:13)
      at processElement (node_modules/@vue/compat/dist/vue.cjs.js:8501:13)
      at patch (node_modules/@vue/compat/dist/vue.cjs.js:8418:21)
      at ReactiveEffect.componentUpdateFn [as fn] (node_modules/@vue/compat/dist/vue.cjs.js:9047:17)
      at ReactiveEffect.run (node_modules/@vue/compat/dist/vue.cjs.js:753:25)
      at instance.update (node_modules/@vue/compat/dist/vue.cjs.js:9084:56)
      at callWithErrorHandling (node_modules/@vue/compat/dist/vue.cjs.js:1876:36)
      at flushJobs (node_modules/@vue/compat/dist/vue.cjs.js:2110:17)

PASS tests/jest/ImageCard.test.js
FAIL tests/jest/CardStack.test.js
  ● CardStack › does not render the ImageCard component when there are no images in the queue

    TypeError: Cannot set properties of undefined (setting 'hasOwnProperty')

      58 |
      59 | 	it( 'does not render the ImageCard component when there are no images in the queue', () => {
    > 60 | 		const wrapper = shallowMount( CardStack, {
         | 		                ^
      61 | 			props: {
      62 | 				queue: 'user'
      63 | 			},

      at mount (node_modules/@vue/test-utils/dist/vue-test-utils.cjs.js:8008:27)
      at shallowMount (node_modules/@vue/test-utils/dist/vue-test-utils.cjs.js:8017:12)
      at Object.<anonymous> (tests/jest/CardStack.test.js:60:19)

  ● CardStack › renders the ImageCard component when there are images in the queue

    TypeError: Cannot set properties of undefined (setting 'hasOwnProperty')

      74 | 		getters.currentImage.mockReturnValue( imageData[ 0 ] );
      75 |
    > 76 | 		const wrapper = shallowMount( CardStack, {
         | 		                ^
      77 | 			props: {
      78 | 				queue: 'popular'
      79 | 			},

      at mount (node_modules/@vue/test-utils/dist/vue-test-utils.cjs.js:8008:27)
      at shallowMount (node_modules/@vue/test-utils/dist/vue-test-utils.cjs.js:8017:12)
      at Object.<anonymous> (tests/jest/CardStack.test.js:76:19)

  ● CardStack › dispatches the getImages action when the count of the image queue reaches zero

    TypeError: Cannot set properties of undefined (setting 'hasOwnProperty')

      88 |
      89 | 	it( 'dispatches the getImages action when the count of the image queue reaches zero', () => {
    > 90 | 		const wrapper = shallowMount( CardStack, {
         | 		                ^
      91 | 			props: {
      92 | 				queue: 'popular'
      93 | 			},

      at mount (node_modules/@vue/test-utils/dist/vue-test-utils.cjs.js:8008:27)
      at shallowMount (node_modules/@vue/test-utils/dist/vue-test-utils.cjs.js:8017:12)
      at Object.<anonymous> (tests/jest/CardStack.test.js:90:19)

Test Suites: 1 failed, 5 passed, 6 total
Tests:       3 failed, 4 skipped, 51 passed, 58 total
Snapshots:   0 total
Time:        4.591 s
Ran all test suites.
--- stdout ---

> test
> grunt test && npm run test:unit

Running "eslint:all" (eslint) task

/src/repo/jest.setup.js
    2:1  warning  ES2015 block-scoped variables are forbidden  es-x/no-block-scoped-variables
    3:1  warning  ES2015 block-scoped variables are forbidden  es-x/no-block-scoped-variables
    3:7  warning  ES2015 destructuring is forbidden            es-x/no-destructuring
   34:1  warning  ES2015 block-scoped variables are forbidden  es-x/no-block-scoped-variables
   39:1  warning  ES2015 block-scoped variables are forbidden  es-x/no-block-scoped-variables
  136:1  warning  ES2015 block-scoped variables are forbidden  es-x/no-block-scoped-variables

/src/repo/resources/components/App.vue
    2:2   warning  The '<wbmad-fade-in>' component has been used, but not defined                          vue/no-undef-components
    5:5   warning  The '<mw-toast-notification>' component has been used, but not defined                  vue/no-undef-components
   19:5   warning  The '<wbmad-image-exclusion-notice>' component has been used, but not defined           vue/no-undef-components
   26:5   warning  The '<tabs>' component has been used, but not defined                                   vue/no-undef-components
   28:6   warning  The '<tab>' component has been used, but not defined                                    vue/no-undef-components
   29:7   warning  The '<card-stack>' component has been used, but not defined                             vue/no-undef-components
   33:6   warning  The '<tab>' component has been used, but not defined                                    vue/no-undef-components
   34:7   warning  The '<personal-uploads-count>' component has been used, but not defined                 vue/no-undef-components
   35:7   warning  The '<card-stack>' component has been used, but not defined                             vue/no-undef-components
  101:1   warning  The `// @vue/component` directive should be included on the line before module.exports  mediawiki/vue-exports-component-directive
  209:18  warning  URL.hash() is not supported in IE 11                                                    compat/compat
  209:18  warning  URL is not supported in IE 11                                                           compat/compat

/src/repo/resources/components/CardStack.vue
   92:16  warning  'images' is not defined                 vue/no-undef-properties
  101:16  warning  'fetchPending' is not defined           vue/no-undef-properties
  110:16  warning  'fetchError' is not defined             vue/no-undef-properties
  121:16  warning  'currentTab' is not defined             vue/no-undef-properties
  121:50  warning  'currentImage' is not defined           vue/no-undef-properties
  138:16  warning  'userStats' is not defined              vue/no-undef-properties
  148:16  warning  'isUserTab' is not defined              vue/no-undef-properties
  148:34  warning  'userHasLabeledUploads' is not defined  vue/no-undef-properties
  178:9   warning  'updateCurrentTab' is not defined       vue/no-undef-properties
  189:3   warning  'imagesInQueue' is not defined          vue/no-undef-properties
  191:10  warning  'getImages' is not defined              vue/no-undef-properties

/src/repo/resources/components/CategoriesList.vue
  31:16  warning  'categories' is not defined    vue/no-undef-properties
  38:16  warning  'currentImage' is not defined  vue/no-undef-properties

/src/repo/resources/components/ImageCard.vue
  133:16  warning  'currentImageTitle' is not defined        vue/no-undef-properties
  140:16  warning  'currentImage' is not defined             vue/no-undef-properties
  154:16  warning  'categories' is not defined               vue/no-undef-properties
  170:14  warning  'confirmedSuggestions' is not defined     vue/no-undef-properties
  170:54  warning  'publishPending' is not defined           vue/no-undef-properties
  190:16  warning  'currentImageSuggestions' is not defined  vue/no-undef-properties
  227:9   warning  'confirmTagsDialog' is not defined        vue/no-undef-properties
  231:18  warning  'thumbUrl' is not defined                 vue/no-undef-properties
  232:20  warning  'imgTitle' is not defined                 vue/no-undef-properties
  241:9   warning  'windowManager' is not defined            vue/no-undef-properties
  255:9   warning  'publishTags' is not defined              vue/no-undef-properties
  263:9   warning  'skipImage' is not defined                vue/no-undef-properties
  272:9   warning  'addCustomTagDialog' is not defined       vue/no-undef-properties
  272:44  warning  'wikidataIds' is not defined              vue/no-undef-properties

/src/repo/resources/components/Spinner.vue
  1:1  warning  Component name "Spinner" should always be multi-word  vue/multi-word-component-names

/src/repo/resources/components/SuggestionsGroup.vue
  75:47  warning  'tagDetailsExpanded' is not defined  vue/no-undef-properties

/src/repo/resources/components/base/Button.vue
    1:1  warning  Component name "Button" should always be multi-word    vue/multi-word-component-names
  219:1  warning  This line has a length of 106. Maximum allowed is 100  max-len

/src/repo/resources/components/base/Icon.vue
  1:1  warning  Component name "Icon" should always be multi-word  vue/multi-word-component-names

/src/repo/resources/components/base/Message.vue
  1:1  warning  Component name "Message" should always be multi-word  vue/multi-word-component-names

/src/repo/resources/components/base/Suggestion.vue
  1:1  warning  Component name "Suggestion" should always be multi-word  vue/multi-word-component-names

/src/repo/resources/components/base/Tab.vue
   1:1  warning  Component name "Tab" should always be multi-word  vue/multi-word-component-names
  28:3  warning  'title' of property found, but never used         vue/no-unused-properties
  33:3  warning  'disabled' of property found, but never used      vue/no-unused-properties

/src/repo/resources/components/base/Tabs.vue
    1:1   warning  Component name "Tabs" should always be multi-word  vue/multi-word-component-names
  111:19  warning  ES2015 'Array.prototype.keys' method is forbidden  es-x/no-array-prototype-keys
  119:20  warning  ES2015 'Array.prototype.keys' method is forbidden  es-x/no-array-prototype-keys
  126:19  warning  ES2015 'Array.prototype.keys' method is forbidden  es-x/no-array-prototype-keys
  151:54  warning  ES2015 'Array.prototype.keys' method is forbidden  es-x/no-array-prototype-keys

/src/repo/resources/store/getters.js
  11:10  warning  ES2015 'Array.prototype.keys' method is forbidden  es-x/no-array-prototype-keys

/src/repo/resources/store/utils.js
  10:13  warning  ES2015 'Array.prototype.keys' method is forbidden  es-x/no-array-prototype-keys

/src/repo/resources/widgets/EntityAutocompleteInputWidget.js
  138:12  warning  ES2015 'Array.prototype.keys' method is forbidden  es-x/no-array-prototype-keys
  143:31  warning  ES2015 'Array.prototype.keys' method is forbidden  es-x/no-array-prototype-keys

/src/repo/resources/widgets/base/FormatValueElement.js
  34:11  warning  ES2015 'Array.prototype.keys' method is forbidden  es-x/no-array-prototype-keys

✖ 65 problems (0 errors, 65 warnings)


Running "banana:MachineVision" (banana) task
>> 2 message directories checked.

Running "stylelint:all" (stylelint) task
>> Linted 27 files without errors

Done.

> test:unit
> jest

---------------------------|---------|----------|---------|---------|-------------------------
File                       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s       
---------------------------|---------|----------|---------|---------|-------------------------
All files                  |   73.19 |    64.21 |   68.91 |   73.19 |                         
 components                |   76.42 |    57.57 |   68.62 |   76.42 |                         
  App.vue                  |   55.81 |    38.46 |   46.15 |   55.81 | 149,192-257,299         
  CardStack.vue            |   77.27 |       60 |   72.72 |   77.27 | 168-191                 
  CardStackPlaceholder.vue |     100 |      100 |     100 |     100 |                         
  CategoriesList.vue       |     100 |      100 |     100 |     100 |                         
  FadeIn.vue               |     100 |      100 |     100 |     100 |                         
  ImageCard.vue            |      85 |      100 |   72.22 |      85 | 154-161,201-202,272-273 
  ImageExclusionNotice.vue |      70 |      100 |      75 |      70 | 49-51                   
  PersonalUploadsCount.vue |     100 |      100 |     100 |     100 |                         
  Spinner.vue              |     100 |      100 |     100 |     100 |                         
  SuggestionsGroup.vue     |     100 |       50 |     100 |     100 | 80-89                   
  UserMessage.vue          |     100 |      100 |     100 |     100 |                         
 components/base           |   64.78 |    41.66 |   72.72 |   64.78 |                         
  Button.vue               |     100 |    66.66 |     100 |     100 | 83                      
  Icon.vue                 |     100 |      100 |     100 |     100 |                         
  Message.vue              |     100 |      100 |     100 |     100 |                         
  Suggestion.vue           |     100 |      100 |     100 |     100 |                         
  Tab.vue                  |     100 |      100 |     100 |     100 |                         
  Tabs.vue                 |      50 |     37.5 |   66.66 |      50 | 75-79,111-134,163,177   
  ToastNotification.vue    |   14.28 |      100 |       0 |   14.28 | 51-76                   
  ToggleSwitch.vue         |      50 |        0 |      75 |      50 | 91-96                   
 models                    |     100 |       75 |     100 |     100 |                         
  Image.js                 |     100 |      100 |     100 |     100 |                         
  Suggestion.js            |     100 |       75 |     100 |     100 | 17                      
 plugins                   |       0 |        0 |       0 |       0 |                         
  api.js                   |       0 |        0 |       0 |       0 | 13-28                   
  logger.js                |       0 |        0 |       0 |       0 | 13-36                   
 store                     |   75.81 |    80.95 |   69.49 |   75.81 |                         
  actions.js               |   88.88 |       95 |   82.75 |   88.88 | 186,305-344             
  getters.js               |   59.09 |       50 |      60 |   59.09 | 33,49,65-97             
  index.js                 |       0 |      100 |     100 |       0 | 3-12                    
  mutations.js             |    62.5 |    66.66 |   47.05 |    62.5 | 43-47,73,129-181        
  state.js                 |       0 |      100 |     100 |       0 | 3                       
  utils.js                 |     100 |      100 |     100 |     100 |                         
---------------------------|---------|----------|---------|---------|-------------------------

--- end ---
Traceback (most recent call last):
  File "/venv/lib/python3.9/site-packages/runner-0.1.0-py3.9.egg/runner/__init__.py", line 1400, in main
    libup.run(args.repo, args.output, args.branch)
  File "/venv/lib/python3.9/site-packages/runner-0.1.0-py3.9.egg/runner/__init__.py", line 1338, in run
    self.npm_upgrade(plan)
  File "/venv/lib/python3.9/site-packages/runner-0.1.0-py3.9.egg/runner/__init__.py", line 1049, in npm_upgrade
    self.npm_test()
  File "/venv/lib/python3.9/site-packages/runner-0.1.0-py3.9.egg/runner/__init__.py", line 287, in npm_test
    self.check_call(['npm', 'test'])
  File "/venv/lib/python3.9/site-packages/runner-0.1.0-py3.9.egg/runner/shell2.py", line 54, in check_call
    res.check_returncode()
  File "/usr/lib/python3.9/subprocess.py", line 460, in check_returncode
    raise CalledProcessError(self.returncode, self.args, self.stdout,
subprocess.CalledProcessError: Command '['/usr/bin/npm', 'test']' returned non-zero exit status 1.
Source code is licensed under the AGPL.