mediawiki/extensions/WikibaseMediaInfo (master)

sourcepatches
$ date
Thu Mar  4 09:48:35 UTC 2021

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

$ git config user.name libraryupgrader

$ git config user.email tools.libraryupgrader@tools.wmflabs.org

$ git submodule update --init

$ grr init
Installed commit-msg hook.

$ git show-ref refs/heads/master
fa01b5c8858c67b858db0bcef668d392af96154d refs/heads/master

$ composer install
Loading composer repositories with package information
Warning from https://repo.packagist.org: You are using an outdated version of Composer. Composer 2 is now available and you should upgrade. See https://getcomposer.org/2
Updating dependencies (including require-dev)
Package operations: 39 installs, 0 updates, 0 removals
  - Installing php-parallel-lint/php-console-color (v0.3): Loading from cache
  - Installing php-parallel-lint/php-console-highlighter (v0.5): Loading from cache
  - Installing php-parallel-lint/php-parallel-lint (v1.2.0): Loading from cache
  - Installing symfony/polyfill-php80 (v1.22.1): Loading from cache
  - Installing symfony/polyfill-mbstring (v1.22.1): Loading from cache
  - Installing symfony/polyfill-intl-normalizer (v1.22.1): Loading from cache
  - Installing symfony/polyfill-intl-grapheme (v1.22.1): Loading from cache
  - Installing symfony/polyfill-ctype (v1.22.1): Loading from cache
  - Installing symfony/string (v5.2.3): Loading from cache
  - Installing psr/container (1.0.0): Loading from cache
  - Installing symfony/service-contracts (v2.2.0): Loading from cache
  - Installing symfony/polyfill-php73 (v1.22.1): Loading from cache
  - Installing symfony/console (v5.2.3): Loading from cache
  - Installing mediawiki/minus-x (1.1.0): Loading from cache
  - Installing squizlabs/php_codesniffer (3.5.8): Loading from cache
  - Installing composer/spdx-licenses (1.5.5): Loading from cache
  - Installing composer/semver (3.2.4): Loading from cache
  - Installing mediawiki/mediawiki-codesniffer (v35.0.0): Loading from cache
  - Installing psr/log (1.1.3): Loading from cache
  - Installing sabre/event (5.1.2): Loading from cache
  - Installing netresearch/jsonmapper (v2.1.0): Loading from cache
  - Installing microsoft/tolerant-php-parser (v0.0.23): Loading from cache
  - Installing phpdocumentor/reflection-common (2.2.0): Loading from cache
  - Installing webmozart/assert (1.9.1): Loading from cache
  - Installing phpdocumentor/type-resolver (1.4.0): Loading from cache
  - Installing phpdocumentor/reflection-docblock (5.2.2): Loading from cache
  - Installing felixfbecker/advanced-json-rpc (v3.2.0): Loading from cache
  - Installing composer/xdebug-handler (1.4.5): Loading from cache
  - Installing phan/phan (3.2.6): Loading from cache
  - Installing mediawiki/phan-taint-check-plugin (3.2.1): Loading from cache
  - Installing mediawiki/mediawiki-phan-config (0.10.6): Loading from cache
  - Installing serialization/serialization (4.0.0): Loading from cache
  - Installing data-values/data-values (2.3.0): Loading from cache
  - Installing data-values/serialization (1.2.3): Loading from cache
  - Installing wikimedia/assert (v0.5.0): Loading from cache
  - Installing wikibase/data-model (9.5.1): Loading from cache
  - Installing wikibase/data-model-serialization (2.9.1): Loading from cache
  - Installing diff/diff (3.2.0): Loading from cache
  - Installing wikibase/data-model-services (5.2.0): Loading from cache
symfony/service-contracts suggests installing symfony/service-implementation
symfony/console suggests installing symfony/event-dispatcher
symfony/console suggests installing symfony/lock
symfony/console suggests installing symfony/process
phan/phan suggests installing ext-ast (Needed for parsing ASTs (unless --use-fallback-parser is used). 1.0.1+ is needed, 1.0.8+ is recommended.)
wikibase/data-model-serialization suggests installing data-values/geo (Needed for deserialization of geographical values)
wikibase/data-model-serialization suggests installing data-values/number (Needed for deserialization of numerical values)
wikibase/data-model-serialization suggests installing data-values/time (Needed for deserialization of time values)
Writing lock file
Generating autoload files
12 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

Upgrading n:eslint-config-wikimedia from 0.17.0 -> 0.18.1
Upgrading n:grunt-stylelint from 0.15.0 -> 0.16.0
Upgrading n:stylelint-config-wikimedia from 0.10.1 -> 0.10.3
$ npm install

> fibers@4.0.3 install /src/repo/node_modules/fibers
> node build.js || nodejs build.js

`linux-x64-64-glibc` exists; testing
Binary is fine; exiting

> chromedriver@2.46.0 install /src/repo/node_modules/chromedriver
> node install.js

Current existing ChromeDriver binary is unavailable, proceding with download and extraction.
Downloading from file:  https://chromedriver.storage.googleapis.com/2.46/chromedriver_linux64.zip
Saving to file: /tmp/2.46/chromedriver/chromedriver_linux64.zip
Received 781K...
Received 1568K...
Received 2352K...
Received 3136K...
Received 3920K...
Received 4704K...
Received 5277K total.
Extracting zip contents
Copying to target path /src/repo/node_modules/chromedriver/lib/chromedriver
Fixing file permissions
Done. ChromeDriver binary available at /src/repo/node_modules/chromedriver/lib/chromedriver/chromedriver

> core-js@3.9.1 postinstall /src/repo/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"

Thank you for using core-js ( https://github.com/zloirock/core-js ) for polyfilling JavaScript standard library!

The project needs your help! Please consider supporting of core-js on Open Collective or Patreon: 
> https://opencollective.com/core-js 
> https://www.patreon.com/zloirock 

Also, the author of core-js ( https://github.com/zloirock ) is looking for a good job -)


> ejs@3.1.3 postinstall /src/repo/node_modules/ejs
> node --harmony ./postinstall.js

Thank you for installing EJS: built with the Jake JavaScript build tool (https://jakejs.com/)


> wikibase-media-info@0.1.0 install /src/repo
> rm -rf node_modules/mediawiki && git clone -q --depth=1 https://gerrit.wikimedia.org/r/mediawiki/core node_modules/mediawiki

npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.1.3 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

added 1177 packages from 1009 contributors and audited 1180 packages in 214.217s

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

found 11 vulnerabilities (2 low, 9 moderate)
  run `npm audit fix` to fix them, or `npm audit` for details

$ npm update eslint -depth 10
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.1.3 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

+ eslint@7.21.0
added 12 packages from 6 contributors, removed 12 packages, updated 14 packages, moved 3 packages and audited 1181 packages in 72.032s

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

found 11 vulnerabilities (2 low, 9 moderate)
  run `npm audit fix` to fix them, or `npm audit` for details

$ npm install grunt-eslint@23.0.0 --save-exact
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.1.3 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

+ grunt-eslint@23.0.0
removed 1 package, updated 1 package and audited 1180 packages in 67.746s

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

found 11 vulnerabilities (2 low, 9 moderate)
  run `npm audit fix` to fix them, or `npm audit` for details

$ ./node_modules/.bin/eslint i18n/be.json i18n/mk.json i18n/fa.json i18n/api/br.json tests/node-qunit/support/fixtures/data/mediaInfoEntity.json i18n/bcl.json i18n/hi.json i18n/hil.json resources/getDeserializer.js i18n/trv.json resources/mediasearch-vue/components/results/ImageResult.vue tests/node-qunit/mediainfo/statements/LinkNoticeWidget.test.js resources/mediasearch-vue/components/base/Dialog.vue i18n/tl.json resources/mediasearch-vue/components/NoResults.vue i18n/api/lb.json i18n/th.json resources/datamodel/MediaInfo.js i18n/ru.json i18n/ksh.json i18n/lb.json i18n/api/ar.json i18n/api/roa-tara.json resources/statements/inputs/GlobeCoordinateInputWidget.js i18n/ml.json i18n/kiu.json resources/statements/inputs/UnsupportedInputWidget.js i18n/kk-cyrl.json i18n/eo.json i18n/id.json i18n/sd.json resources/mediasearch-vue/components/results/OtherResult.vue resources/statements/inputs/TimeInputWidget.js resources/mediasearch-vue/components/App.vue i18n/eu.json tests/phpunit/data/entities/M1.json resources/filepage/StatementPanel.js resources/mediawiki.template.mustache+dom.js resources/mediasearch-vue/components/base/AutocompleteSearchInput.vue resources/mediasearch-vue/store/state.js i18n/hy.json i18n/lt.json i18n/ja.json i18n/tly.json i18n/api/bn.json i18n/syl.json i18n/hsb.json resources/polyfills/Array.prototype.find.js i18n/fr.json resources/mediasearch-vue/models/SearchFilter.js tests/node-qunit/mediainfo/statements/AddPropertyWidget.test.js i18n/oc.json resources/README/1.ExampleComponentWidget.js i18n/tt-cyrl.json resources/mediasearch-vue/plugins/eventLogger.js i18n/es.json tests/node-qunit/mediainfo/statements/inputs/EntityInputWidget.test.js i18n/vec.json resources/__namespace.js resources/statements/StatementWidget.js tests/node-qunit/mediainfo/base/DOMLessGroupWidget.test.js resources/base/index.js resources/filepage/LicenseDialogWidget.js i18n/ta.json i18n/et.json i18n/bcc.json i18n/api/nl.json resources/statements/inputs/index.js i18n/api/fi.json composer.json tests/node-qunit/support/helpers.js tests/node-qunit/mediainfo/statements/ItemWidget.test.js i18n/nl.json i18n/lg.json i18n/la.json i18n/vi.json i18n/api/it.json i18n/pnb.json tests/selenium/pageobjects/edit.page.js resources/mediasearch-vue/components/Spinner.vue i18n/ro.json i18n/api/he.json i18n/yi.json i18n/api/nb.json resources/statements/index.js i18n/hu.json i18n/sk.json i18n/lv.json i18n/fi.json resources/mediasearch-vue/components/DidYouMean.vue resources/mediasearch-vue/components/results/VideoResult.vue tests/selenium/pageobjects/login.page.js resources/README/3.BestPractices.js i18n/pt.json i18n/api/en-gb.json i18n/az.json tests/node-qunit/mediainfo/README/1.ExampleComponentWidget.test.js i18n/ne.json i18n/pl.json i18n/ko.json tests/node-qunit/support/fixtures/data/wbmiPropertyTypes.json tests/node-qunit/mediainfo/statements/StatementWidget.test.js extension.json i18n/lmo.json i18n/cy.json resources/statements/ItemWidget.js i18n/ug-arab.json i18n/de.json i18n/en-ca.json resources/mediasearch-vue/components/base/Observer.vue i18n/om.json resources/search/PropertySuggestionsWidget.js tests/node-qunit/support/fixtures/data/wbDataTypes.json i18n/kjp.json i18n/diq.json i18n/jv.json i18n/fit.json i18n/xsy.json resources/statements/inputs/EntityInputWidget.js tests/node-qunit/mediainfo/README/2.TemplatingFeatures.test.js i18n/gl.json i18n/it.json i18n/uk.json tests/node-qunit/mediainfo/statements/inputs/QuantityInputWidget.test.js tests/node-qunit/mediainfo/filepage/CaptionsPanel.test.js i18n/bs.json i18n/api/pt-br.json i18n/skr-arab.json i18n/sco.json resources/search/index.js resources/filepage/CaptionsPanel.js i18n/si.json tests/phpunit/data/entities_search/dog.json resources/statements/LinkNoticeWidget.js i18n/api/pl.json tests/node-qunit/mediainfo/filepage/StatementPanel.test.js i18n/kum.json tests/node-qunit/mediainfo/filepage/LicenseDialogWidget.test.js i18n/api/fr.json resources/base/DOMLessGroupWidget.js i18n/el.json i18n/ca.json tests/selenium/wdio.conf.js resources/mediasearch-vue/components/base/LookupResults.vue i18n/api/io.json i18n/ce.json i18n/qqq.json i18n/sv.json Gruntfile.js tests/selenium/specs_betacommons/loggedInUserCanEdit.js i18n/nds.json i18n/nap.json i18n/api/en.json i18n/cs.json i18n/api/sv.json resources/mediasearch-vue/components/results/PageResult.vue i18n/nn.json resources/mediasearch-vue/components/base/Image.vue package-lock.json resources/README/2.TemplatingFeatures.js i18n/be-tarask.json resources/mediasearch-vue/store/index.js i18n/bn.json i18n/ku-latn.json resources/UlsWidget.js resources/mediasearch-vue/components/base/Select.vue tests/node-qunit/support/fixtures/data/coordinateData.js resources/README/index.js resources/serialization/MediaInfoDeserializer.js resources/statements/inputs/EntityAutocompleteInputWidget.js i18n/ia.json resources/statements/inputs/AbstractInputWidget.js i18n/sw.json i18n/dsb.json resources/mediasearch-vue/components/base/Tabs.vue i18n/gom-latn.json i18n/my.json i18n/bpy.json resources/mediasearch-vue/store/mutations.js i18n/roa-tara.json resources/base/ComponentWidget.js i18n/khw.json i18n/ab.json resources/mediasearch-vue/components/SearchResults.vue resources/mediasearch-vue/store/getters.js tests/selenium/support/js-image-generator.js resources/base/FormatValueElement.js i18n/api/ko.json i18n/en-gb.json i18n/as.json resources/mediasearch-vue/mixins/searchResultTimeBased.js i18n/kcg.json resources/filepage/CaptionData.js i18n/api/qqq.json i18n/nds-nl.json resources/statements/ConstraintsReportHandlerElement.js resources/statements/inputs/MultiTypeInputWrapperWidget.js resources/statements/config/wbMonolingualTextLanguages.json resources/mediasearch-vue/components/base/SelectMenu.vue resources/mediasearch-vue/components/SearchFilters.vue lib/icons.js resources/statements/inputs/StringInputWidget.js i18n/da.json resources/mediasearch-vue/components/QuickView.vue i18n/mnw.json i18n/hr.json i18n/gor.json i18n/ba.json tests/node-qunit/mediainfo/statements/QualifierWidget.test.js resources/mediasearch-vue/components/ConceptChips.vue i18n/es-formal.json tests/selenium/specs_betacommons/structuredDataOnFilepage.js resources/statements/inputs/QuantityInputWidget.js i18n/lij.json resources/filepage/init.js i18n/en.json i18n/tcy.json i18n/ms.json i18n/zh-hans.json tests/node-qunit/support/fixtures/data/paneldata-empty.json tests/node-qunit/mediainfo/statements/inputs/MonolingualTextInputWidget.test.js i18n/ps.json i18n/zh-hant.json i18n/ti.json i18n/nb.json resources/filepage/CaptionDataEditor.js resources/mediasearch-vue/components/results/AudioResult.vue tests/node-qunit/support/fixtures/data/qualifierMenuOptionData.json tests/node-qunit/mediainfo/mediainfo.template.mustache+dom.test.js resources/mediasearch-vue/components/base/Player.vue resources/filepage/ProtectionMsgWidget.js i18n/api/eu.json i18n/api/ca.json i18n/tr.json i18n/szl.json i18n/zgh.json i18n/mrh.json tests/node-qunit/mediainfo/statements/inputs/TimeInputWidget.test.js resources/mediasearch-vue/components/EndOfResults.vue i18n/bg.json i18n/ckb.json i18n/api/fa.json i18n/api/mk.json i18n/sr-ec.json i18n/hoc.json i18n/sh.json resources/statements/config/wbTermsLanguages.json resources/statements/QualifierWidget.js i18n/bto.json resources/mediasearch-vue/components/base/Tab.vue i18n/api/zh-hant.json i18n/pa.json i18n/api/uk.json tests/node-qunit/mediainfo/base/ComponentWidget.test.js resources/mediasearch-vue/store/actions.js i18n/api/de.json i18n/dag.json tests/node-qunit/mediainfo/statements/inputs/MultiTypeInputWrapperWidget.test.js i18n/brh.json i18n/ann.json tests/selenium/specs_betacommons/loggedInUserCanUpload.js resources/mediasearch-vue/init.js resources/filepage/AnonWarning.js tests/node-qunit/mediainfo/README/3.BestPractices.test.js i18n/api/es.json resources/mediasearch-vue/components/base/Message.vue resources/mediasearch-vue/components/base/Icon.vue i18n/yue.json i18n/xmf.json i18n/gu.json resources/statements/AddPropertyWidget.js i18n/hyw.json resources/filepage/CaptionsEditActionsWidget.js tests/node-qunit/support/hooks.js resources/mediasearch-vue/mixins/searchResult.js i18n/sl.json tests/selenium/pageobjects/file.page.js i18n/bho.json i18n/ar.json tests/selenium/pageobjects/upload.page.js i18n/io.json resources/mediasearch-vue/mixins/autocompleteLookupHandler.js tests/phpunit/data/entities_search/cat.json tests/node-qunit/mediainfo/filepage/ProtectionMsgWidget.test.js resources/mediasearch-vue/components/base/CopyTextLayout.vue resources/statements/inputs/MonolingualTextInputWidget.js resources/mediasearch-vue/components/EmptyState.vue package.json i18n/pt-br.json i18n/api/tr.json i18n/ast.json i18n/mni.json resources/statements/config/index.js resources/mediasearch-vue/components/base/Button.vue i18n/he.json tests/node-qunit/mediainfo/statements/inputs/GlobeCoordinateInputWidget.test.js i18n/te.json tests/node-qunit/mediainfo/statements/inputs/StringInputWidget.test.js resources/filepage/CancelPublishWidget.js i18n/ary.json resources/polyfills/Array.prototype.findIndex.js i18n/br.json --fix

/src/repo/resources/datamodel/MediaInfo.js
  24:0  warning  Syntax error in namepath: 
[statementGroupSet=new datamodel.StatementGroupSet()]  jsdoc/valid-types

/src/repo/resources/filepage/CaptionsPanel.js
  40:0  warning  Invalid JSDoc tag name "mixins"  jsdoc/check-tag-names

/src/repo/resources/filepage/StatementPanel.js
  18:0  warning  Invalid JSDoc tag name "mixins"  jsdoc/check-tag-names

/src/repo/resources/mediasearch-vue/components/App.vue
  184:28  error  IntersectionObserverEntry is not supported in Safari 5.1, IE 11  compat/compat

/src/repo/resources/mediasearch-vue/components/SearchFilters.vue
  110:28  error  IntersectionObserverEntry is not supported in Safari 5.1, IE 11  compat/compat

/src/repo/resources/mediasearch-vue/components/base/Image.vue
  52:28  error  IntersectionObserverEntry is not supported in Safari 5.1, IE 11  compat/compat

/src/repo/resources/mediasearch-vue/components/base/Observer.vue
  52:19  error  IntersectionObserver is not supported in Safari 5.1, IE 11, android 4.1  compat/compat

/src/repo/resources/mediasearch-vue/components/base/Tabs.vue
  95:28  error  IntersectionObserverEntry is not supported in Safari 5.1, IE 11  compat/compat

/src/repo/resources/mediasearch-vue/components/results/AudioResult.vue
  16:1  warning  This line has a length of 119. Maximum allowed is 100  max-len

/src/repo/resources/mediasearch-vue/components/results/PageResult.vue
  17:23  warning  'v-html' directive can lead to XSS attack  vue/no-v-html

/src/repo/resources/mediasearch-vue/components/results/VideoResult.vue
  24:1  warning  This line has a length of 123. Maximum allowed is 100  max-len

/src/repo/resources/statements/ItemWidget.js
  448:4  error  MutationObserver.observe() is not supported in Safari 5.1            compat/compat
  448:4  error  MutationObserver is not supported in Safari 5.1, iOS Safari 6.0-6.1  compat/compat

/src/repo/resources/statements/inputs/GlobeCoordinateInputWidget.js
  426:4  error  MutationObserver.observe() is not supported in Safari 5.1            compat/compat
  426:4  error  MutationObserver is not supported in Safari 5.1, iOS Safari 6.0-6.1  compat/compat

/src/repo/tests/node-qunit/mediainfo/base/ComponentWidget.test.js
  9:34  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

/src/repo/tests/node-qunit/mediainfo/base/DOMLessGroupWidget.test.js
  10:37  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

/src/repo/tests/node-qunit/mediainfo/filepage/CaptionsPanel.test.js
  13:68  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

/src/repo/tests/node-qunit/mediainfo/filepage/StatementPanel.test.js
  14:71  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

/src/repo/tests/node-qunit/mediainfo/mediainfo.template.mustache+dom.test.js
  7:50  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

/src/repo/tests/node-qunit/mediainfo/statements/LinkNoticeWidget.test.js
  9:35  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

/src/repo/tests/node-qunit/support/helpers.js
  103:14  error  Map is not supported in Safari 5.1, iOS Safari 6.0-6.1  compat/compat

/src/repo/tests/node-qunit/support/hooks.js
   32:23  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat
   52:28  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat
   77:27  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat
  105:28  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat
  129:31  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

✖ 27 problems (21 errors, 6 warnings)


$ ./node_modules/.bin/eslint i18n/be.json i18n/mk.json i18n/fa.json i18n/api/br.json tests/node-qunit/support/fixtures/data/mediaInfoEntity.json i18n/bcl.json i18n/hi.json i18n/hil.json resources/getDeserializer.js i18n/trv.json resources/mediasearch-vue/components/results/ImageResult.vue tests/node-qunit/mediainfo/statements/LinkNoticeWidget.test.js resources/mediasearch-vue/components/base/Dialog.vue i18n/tl.json resources/mediasearch-vue/components/NoResults.vue i18n/api/lb.json i18n/th.json resources/datamodel/MediaInfo.js i18n/ru.json i18n/ksh.json i18n/lb.json i18n/api/ar.json i18n/api/roa-tara.json resources/statements/inputs/GlobeCoordinateInputWidget.js i18n/ml.json i18n/kiu.json resources/statements/inputs/UnsupportedInputWidget.js i18n/kk-cyrl.json i18n/eo.json i18n/id.json i18n/sd.json resources/mediasearch-vue/components/results/OtherResult.vue resources/statements/inputs/TimeInputWidget.js resources/mediasearch-vue/components/App.vue i18n/eu.json tests/phpunit/data/entities/M1.json resources/filepage/StatementPanel.js resources/mediawiki.template.mustache+dom.js resources/mediasearch-vue/components/base/AutocompleteSearchInput.vue resources/mediasearch-vue/store/state.js i18n/hy.json i18n/lt.json i18n/ja.json i18n/tly.json i18n/api/bn.json i18n/syl.json i18n/hsb.json resources/polyfills/Array.prototype.find.js i18n/fr.json resources/mediasearch-vue/models/SearchFilter.js tests/node-qunit/mediainfo/statements/AddPropertyWidget.test.js i18n/oc.json resources/README/1.ExampleComponentWidget.js i18n/tt-cyrl.json resources/mediasearch-vue/plugins/eventLogger.js i18n/es.json tests/node-qunit/mediainfo/statements/inputs/EntityInputWidget.test.js i18n/vec.json resources/__namespace.js resources/statements/StatementWidget.js tests/node-qunit/mediainfo/base/DOMLessGroupWidget.test.js resources/base/index.js resources/filepage/LicenseDialogWidget.js i18n/ta.json i18n/et.json i18n/bcc.json i18n/api/nl.json resources/statements/inputs/index.js i18n/api/fi.json composer.json tests/node-qunit/support/helpers.js tests/node-qunit/mediainfo/statements/ItemWidget.test.js i18n/nl.json i18n/lg.json i18n/la.json i18n/vi.json i18n/api/it.json i18n/pnb.json tests/selenium/pageobjects/edit.page.js resources/mediasearch-vue/components/Spinner.vue i18n/ro.json i18n/api/he.json i18n/yi.json i18n/api/nb.json resources/statements/index.js i18n/hu.json i18n/sk.json i18n/lv.json i18n/fi.json resources/mediasearch-vue/components/DidYouMean.vue resources/mediasearch-vue/components/results/VideoResult.vue tests/selenium/pageobjects/login.page.js resources/README/3.BestPractices.js i18n/pt.json i18n/api/en-gb.json i18n/az.json tests/node-qunit/mediainfo/README/1.ExampleComponentWidget.test.js i18n/ne.json i18n/pl.json i18n/ko.json tests/node-qunit/support/fixtures/data/wbmiPropertyTypes.json tests/node-qunit/mediainfo/statements/StatementWidget.test.js extension.json i18n/lmo.json i18n/cy.json resources/statements/ItemWidget.js i18n/ug-arab.json i18n/de.json i18n/en-ca.json resources/mediasearch-vue/components/base/Observer.vue i18n/om.json resources/search/PropertySuggestionsWidget.js tests/node-qunit/support/fixtures/data/wbDataTypes.json i18n/kjp.json i18n/diq.json i18n/jv.json i18n/fit.json i18n/xsy.json resources/statements/inputs/EntityInputWidget.js tests/node-qunit/mediainfo/README/2.TemplatingFeatures.test.js i18n/gl.json i18n/it.json i18n/uk.json tests/node-qunit/mediainfo/statements/inputs/QuantityInputWidget.test.js tests/node-qunit/mediainfo/filepage/CaptionsPanel.test.js i18n/bs.json i18n/api/pt-br.json i18n/skr-arab.json i18n/sco.json resources/search/index.js resources/filepage/CaptionsPanel.js i18n/si.json tests/phpunit/data/entities_search/dog.json resources/statements/LinkNoticeWidget.js i18n/api/pl.json tests/node-qunit/mediainfo/filepage/StatementPanel.test.js i18n/kum.json tests/node-qunit/mediainfo/filepage/LicenseDialogWidget.test.js i18n/api/fr.json resources/base/DOMLessGroupWidget.js i18n/el.json i18n/ca.json tests/selenium/wdio.conf.js resources/mediasearch-vue/components/base/LookupResults.vue i18n/api/io.json i18n/ce.json i18n/qqq.json i18n/sv.json Gruntfile.js tests/selenium/specs_betacommons/loggedInUserCanEdit.js i18n/nds.json i18n/nap.json i18n/api/en.json i18n/cs.json i18n/api/sv.json resources/mediasearch-vue/components/results/PageResult.vue i18n/nn.json resources/mediasearch-vue/components/base/Image.vue package-lock.json resources/README/2.TemplatingFeatures.js i18n/be-tarask.json resources/mediasearch-vue/store/index.js i18n/bn.json i18n/ku-latn.json resources/UlsWidget.js resources/mediasearch-vue/components/base/Select.vue tests/node-qunit/support/fixtures/data/coordinateData.js resources/README/index.js resources/serialization/MediaInfoDeserializer.js resources/statements/inputs/EntityAutocompleteInputWidget.js i18n/ia.json resources/statements/inputs/AbstractInputWidget.js i18n/sw.json i18n/dsb.json resources/mediasearch-vue/components/base/Tabs.vue i18n/gom-latn.json i18n/my.json i18n/bpy.json resources/mediasearch-vue/store/mutations.js i18n/roa-tara.json resources/base/ComponentWidget.js i18n/khw.json i18n/ab.json resources/mediasearch-vue/components/SearchResults.vue resources/mediasearch-vue/store/getters.js tests/selenium/support/js-image-generator.js resources/base/FormatValueElement.js i18n/api/ko.json i18n/en-gb.json i18n/as.json resources/mediasearch-vue/mixins/searchResultTimeBased.js i18n/kcg.json resources/filepage/CaptionData.js i18n/api/qqq.json i18n/nds-nl.json resources/statements/ConstraintsReportHandlerElement.js resources/statements/inputs/MultiTypeInputWrapperWidget.js resources/statements/config/wbMonolingualTextLanguages.json resources/mediasearch-vue/components/base/SelectMenu.vue resources/mediasearch-vue/components/SearchFilters.vue lib/icons.js resources/statements/inputs/StringInputWidget.js i18n/da.json resources/mediasearch-vue/components/QuickView.vue i18n/mnw.json i18n/hr.json i18n/gor.json i18n/ba.json tests/node-qunit/mediainfo/statements/QualifierWidget.test.js resources/mediasearch-vue/components/ConceptChips.vue i18n/es-formal.json tests/selenium/specs_betacommons/structuredDataOnFilepage.js resources/statements/inputs/QuantityInputWidget.js i18n/lij.json resources/filepage/init.js i18n/en.json i18n/tcy.json i18n/ms.json i18n/zh-hans.json tests/node-qunit/support/fixtures/data/paneldata-empty.json tests/node-qunit/mediainfo/statements/inputs/MonolingualTextInputWidget.test.js i18n/ps.json i18n/zh-hant.json i18n/ti.json i18n/nb.json resources/filepage/CaptionDataEditor.js resources/mediasearch-vue/components/results/AudioResult.vue tests/node-qunit/support/fixtures/data/qualifierMenuOptionData.json tests/node-qunit/mediainfo/mediainfo.template.mustache+dom.test.js resources/mediasearch-vue/components/base/Player.vue resources/filepage/ProtectionMsgWidget.js i18n/api/eu.json i18n/api/ca.json i18n/tr.json i18n/szl.json i18n/zgh.json i18n/mrh.json tests/node-qunit/mediainfo/statements/inputs/TimeInputWidget.test.js resources/mediasearch-vue/components/EndOfResults.vue i18n/bg.json i18n/ckb.json i18n/api/fa.json i18n/api/mk.json i18n/sr-ec.json i18n/hoc.json i18n/sh.json resources/statements/config/wbTermsLanguages.json resources/statements/QualifierWidget.js i18n/bto.json resources/mediasearch-vue/components/base/Tab.vue i18n/api/zh-hant.json i18n/pa.json i18n/api/uk.json tests/node-qunit/mediainfo/base/ComponentWidget.test.js resources/mediasearch-vue/store/actions.js i18n/api/de.json i18n/dag.json tests/node-qunit/mediainfo/statements/inputs/MultiTypeInputWrapperWidget.test.js i18n/brh.json i18n/ann.json tests/selenium/specs_betacommons/loggedInUserCanUpload.js resources/mediasearch-vue/init.js resources/filepage/AnonWarning.js tests/node-qunit/mediainfo/README/3.BestPractices.test.js i18n/api/es.json resources/mediasearch-vue/components/base/Message.vue resources/mediasearch-vue/components/base/Icon.vue i18n/yue.json i18n/xmf.json i18n/gu.json resources/statements/AddPropertyWidget.js i18n/hyw.json resources/filepage/CaptionsEditActionsWidget.js tests/node-qunit/support/hooks.js resources/mediasearch-vue/mixins/searchResult.js i18n/sl.json tests/selenium/pageobjects/file.page.js i18n/bho.json i18n/ar.json tests/selenium/pageobjects/upload.page.js i18n/io.json resources/mediasearch-vue/mixins/autocompleteLookupHandler.js tests/phpunit/data/entities_search/cat.json tests/node-qunit/mediainfo/filepage/ProtectionMsgWidget.test.js resources/mediasearch-vue/components/base/CopyTextLayout.vue resources/statements/inputs/MonolingualTextInputWidget.js resources/mediasearch-vue/components/EmptyState.vue package.json i18n/pt-br.json i18n/api/tr.json i18n/ast.json i18n/mni.json resources/statements/config/index.js resources/mediasearch-vue/components/base/Button.vue i18n/he.json tests/node-qunit/mediainfo/statements/inputs/GlobeCoordinateInputWidget.test.js i18n/te.json tests/node-qunit/mediainfo/statements/inputs/StringInputWidget.test.js resources/filepage/CancelPublishWidget.js i18n/ary.json resources/polyfills/Array.prototype.findIndex.js i18n/br.json -f json
[{"filePath":"/src/repo/Gruntfile.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/composer.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/extension.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ab.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ann.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/ar.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/bn.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/br.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/ca.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/de.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/en-gb.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/en.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/es.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/eu.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/fa.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/fi.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/fr.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/he.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/io.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/it.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/ko.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/lb.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/mk.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/nb.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/nl.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/pl.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/pt-br.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/qqq.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/roa-tara.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/sv.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/tr.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/uk.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/api/zh-hant.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ar.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ary.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/as.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ast.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/az.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ba.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/bcc.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/bcl.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/be-tarask.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/be.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/bg.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/bho.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/bn.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/bpy.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/br.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/brh.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/bs.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/bto.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ca.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ce.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ckb.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/cs.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/cy.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/da.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/dag.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/de.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/diq.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/dsb.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/el.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/en-ca.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/en-gb.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/en.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/eo.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/es-formal.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/es.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/et.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/eu.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/fa.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/fi.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/fit.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/fr.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/gl.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/gom-latn.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/gor.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/gu.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/he.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/hi.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/hil.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/hoc.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/hr.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/hsb.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/hu.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/hy.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/hyw.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ia.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/id.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/io.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/it.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ja.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/jv.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/kcg.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/khw.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/kiu.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/kjp.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/kk-cyrl.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ko.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ksh.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ku-latn.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/kum.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/la.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/lb.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/lg.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/lij.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/lmo.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/lt.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/lv.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/mk.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ml.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/mni.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/mnw.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/mrh.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ms.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/my.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/nap.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/nb.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/nds-nl.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/nds.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ne.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/nl.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/nn.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/oc.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/om.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/pa.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/pl.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/pnb.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ps.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/pt-br.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/pt.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/qqq.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ro.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/roa-tara.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ru.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sco.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sd.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sh.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/si.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sk.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/skr-arab.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sl.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sr-ec.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sv.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/sw.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/syl.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/szl.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ta.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/tcy.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/te.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/th.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ti.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/tl.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/tly.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/tr.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/trv.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/tt-cyrl.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/ug-arab.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/uk.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/vec.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/vi.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/xmf.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/xsy.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/yi.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/yue.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/zgh.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/zh-hans.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/i18n/zh-hant.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/lib/icons.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/package-lock.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/package.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/README/1.ExampleComponentWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/README/2.TemplatingFeatures.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/README/3.BestPractices.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/README/index.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/UlsWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/__namespace.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/base/ComponentWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/base/DOMLessGroupWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/base/FormatValueElement.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/base/index.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/datamodel/MediaInfo.js","messages":[{"ruleId":"jsdoc/valid-types","severity":1,"message":"Syntax error in namepath: \n[statementGroupSet=new datamodel.StatementGroupSet()]","line":24,"column":null,"nodeType":"Block","endLine":24,"endColumn":null}],"errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * @external datamodel\n */\n\n( function ( util ) {\n\t'use strict';\n\n\t/* eslint-disable no-underscore-dangle */\n\n\tvar datamodel = require( 'wikibase.datamodel' ),\n\t\tPARENT = datamodel.Entity,\n\n\t\t/**\n\t\t * @class datamodel.MediaInfo\n\t\t * @extends datamodel.Entity\n\t\t * @license GPL-2.0-or-later\n\t\t * @author Adrian Heine <adrian.heine@wikimedia.de>\n\t\t *\n\t\t * @constructor\n\t\t *\n\t\t * @param {string} mediaInfoId\n\t\t * @param {datamodel.TermMap|null} [labels=new datamodel.TermMap()]\n\t\t * @param {datamodel.TermMap|null} [descriptions=new datamodel.TermMap()]\n\t\t * @param {datamodel.StatementGroupSet|null}\n\t\t *   [statementGroupSet=new datamodel.StatementGroupSet()]\n\t\t *\n\t\t * @throws {Error} if a required parameter is not specified properly.\n\t\t */\n\t\tSELF = util.inherit(\n\t\t\t'WbDataModelMediaInfo',\n\t\t\tPARENT,\n\t\t\tfunction ( mediaInfoId, labels, descriptions, statementGroupSet ) {\n\t\t\t\tlabels = labels || new datamodel.TermMap();\n\t\t\t\tdescriptions = descriptions || new datamodel.TermMap();\n\t\t\t\tstatementGroupSet = statementGroupSet || new datamodel.StatementGroupSet();\n\n\t\t\t\tif (\n\t\t\t\t\ttypeof mediaInfoId !== 'string' ||\n\t\t\t\t\t!( labels instanceof datamodel.TermMap ) ||\n\t\t\t\t\t!( descriptions instanceof datamodel.TermMap ) ||\n\t\t\t\t\t!( statementGroupSet instanceof datamodel.StatementGroupSet )\n\t\t\t\t) {\n\t\t\t\t\tthrow new Error( 'Required parameter(s) missing or not defined properly' );\n\t\t\t\t}\n\n\t\t\t\tthis._id = mediaInfoId;\n\t\t\t\tthis._statementGroupSet = statementGroupSet;\n\t\t\t\tthis._fingerprint = new datamodel.Fingerprint( labels, descriptions );\n\t\t\t},\n\t\t\t{\n\n\t\t\t\t/**\n\t\t\t\t * @property {datamodel.StatementGroupSet}\n\t\t\t\t * @private\n\t\t\t\t */\n\t\t\t\t_statementGroupSet: null,\n\n\t\t\t\t/**\n\t\t\t\t * @return {datamodel.StatementGroupSet}\n\t\t\t\t */\n\t\t\t\tgetStatements: function () {\n\t\t\t\t\treturn this._statementGroupSet;\n\t\t\t\t},\n\n\t\t\t\t/**\n\t\t\t\t * @return {boolean}\n\t\t\t\t */\n\t\t\t\tisEmpty: function () {\n\t\t\t\t\treturn this._statementGroupSet.isEmpty() && this._fingerprint.isEmpty();\n\t\t\t\t},\n\n\t\t\t\t/**\n\t\t\t\t * @param {*} mediaInfo\n\t\t\t\t * @return {boolean}\n\t\t\t\t */\n\t\t\t\tequals: function ( mediaInfo ) {\n\t\t\t\t\treturn mediaInfo === this ||\n\t\t\t\t\t( mediaInfo instanceof SELF &&\n\t\t\t\t\t\tthis._id === mediaInfo.getId() &&\n\t\t\t\t\t\tthis._statementGroupSet.equals( mediaInfo.getStatements() ) &&\n\t\t\t\t\t\tthis._fingerprint.equals( mediaInfo.getFingerprint() )\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} );\n\n\t/**\n\t * @inheritdoc\n\t * @property {string} [TYPE='mediainfo']\n\t * @static\n\t */\n\tSELF.TYPE = 'mediainfo';\n\n\tmodule.exports = SELF;\n\n}( util ) );\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/filepage/AnonWarning.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/filepage/CancelPublishWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/filepage/CaptionData.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/filepage/CaptionDataEditor.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/filepage/CaptionsEditActionsWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/filepage/CaptionsPanel.js","messages":[{"ruleId":"jsdoc/check-tag-names","severity":1,"message":"Invalid JSDoc tag name \"mixins\".","line":40,"column":null,"nodeType":"Block","endLine":40,"endColumn":null}],"errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use strict';\n\n/**\n * @external datamodel\n */\n\nvar AnonWarning = require( './AnonWarning.js' ),\n\tCaptionData = require( './CaptionData.js' ),\n\tCaptionDataEditor = require( './CaptionDataEditor.js' ),\n\tCaptionsEditActionsWidget = require( './CaptionsEditActionsWidget.js' ),\n\tLicenseDialogWidget = require( './LicenseDialogWidget.js' ),\n\tComponentWidget = require( 'wikibase.mediainfo.base' ).ComponentWidget,\n\twbTermsLanguages = require( 'wikibase.mediainfo.statements' ).config.wbTermsLanguages,\n\tCaptionsPanel;\n\n/**\n * Panel for displaying/editing structured data multi-lingual captions\n *\n * RULES FOR LANGUAGE ORDERING/DISPLAY\n *\n * Order\n * -----\n *\n * 1. Show a caption for the interface language of the page (whether or not it has a value)\n * 2. If there is no caption for the interface language, show the first caption in the fallback\n *    chain that has a value (if any)\n * 3. If the logged-in user has Babel languages, and they haven’t already been shown, then show\n *    captions for all of them next, whether or not they have values\n * 4. Show everything else with a value\n *\n * Display\n * -------\n *\n * 1, 2, 3 are always displayed\n * 4 are hidden/shown by the languagesViewWidget\n * ... or, in other words - the first caption is always shown, the first non-blank caption\n * is always shown, all user languages are always shown, and everything else may be hidden.\n *\n * @extends OO.ui.Element\n * @mixins OO.ui.mixin.PendingElement\n *\n * @constructor\n * @param {Object} [config]\n * @param {datamodel.MediaInfo} config.mediaInfo\n * @param {boolean} [config.canEdit] True if the captions should be editable on the page\n * @param {string[]} [config.userLanguages] The language the user has indicated that they use (via babel)\n * @param {string[]} [config.languageFallbackChain]\n * @param {number} [config.warnWithinMaxCaptionLength] Show a warning when the caption length is within X\n *   characters of the max\n */\nCaptionsPanel = function ( config ) {\n\tvar self = this;\n\tconfig = config || {};\n\n\t// Parent constructor\n\tCaptionsPanel.parent.call( this, config );\n\n\t// Mixin constructor\n\tOO.ui.mixin.PendingElement.call( this, config );\n\n\tthis.userLanguages = config.userLanguages || [];\n\tthis.languageFallbackChain = config.languageFallbackChain || [ 'en' ];\n\tthis.canEdit = config.canEdit || true;\n\tthis.warnWithinMaxCaptionLength = config.warnWithinMaxCaptionLength;\n\tthis.savedCaptionsData = this.captionsDataFromMediaInfoEntity( config.mediaInfo );\n\n\tthis.api = wikibase.api.getLocationAgnosticMwApi(\n\t\tmw.config.get(\n\t\t\t'wbmiRepoApiUrl',\n\t\t\tmw.config.get( 'wbRepoApiUrl' )\n\t\t)\n\t);\n\n\t// Create the various widgets\n\tthis.licenseDialogWidget = new LicenseDialogWidget();\n\tthis.editActionsWidget = new CaptionsEditActionsWidget();\n\tthis.editActionsWidget.connect( this, { add: 'addNewEmptyLanguageRow' } );\n\tthis.editActionsWidget.connect( this, { publish: 'sendData' } );\n\tthis.editActionsWidget.connect( this, { cancel: 'onCancel' } );\n\n\tthis.state = $.extend(\n\t\t{},\n\t\tthis.getCaptionsState( this.savedCaptionsData ),\n\t\t{\n\t\t\tediting: false,\n\t\t\tdisabled: false,\n\t\t\tcaptionsDataEditors: {},\n\t\t\tdisplayAllLanguages: false\n\t\t}\n\t);\n\n\tComponentWidget.call(\n\t\tthis,\n\t\t'wikibase.mediainfo.filePageDisplay',\n\t\t'templates/filepage/CaptionsPanel.mustache+dom'\n\t);\n\n\t// Set the target pending element to first child of $element, which is the first node of the\n\t// rendered template\n\tthis.renderPromise.then( function () {\n\t\tself.$pending = self.$element.children( ':first' );\n\t} );\n};\n\n/* Inheritance */\nOO.inheritClass( CaptionsPanel, OO.ui.Widget );\nOO.mixinClass( CaptionsPanel, ComponentWidget );\nOO.mixinClass( CaptionsPanel, OO.ui.mixin.PendingElement );\n\n/**\n * @param {datamodel.MediaInfo} mediaInfo\n * @return {Object} An object with langCodes as keys and CaptionData objects as values\n * @private\n */\nCaptionsPanel.prototype.captionsDataFromMediaInfoEntity = function ( mediaInfo ) {\n\tvar captionsData = {};\n\tif ( mediaInfo.labels !== undefined ) {\n\t\tObject.keys( mediaInfo.labels ).forEach( function ( langCode ) {\n\t\t\tcaptionsData[ langCode ] = new CaptionData(\n\t\t\t\tlangCode,\n\t\t\t\tmediaInfo.labels[ langCode ].value\n\t\t\t);\n\t\t} );\n\t}\n\treturn captionsData;\n};\n\n/**\n * @param {Object} captionsData An object with langCodes as keys and CaptionData objects as values\n * @return {{captionsData:Object, orderedLanguageCodes:Array}}\n */\nCaptionsPanel.prototype.getCaptionsState = function ( captionsData ) {\n\t// ensure we have the interface language\n\tcaptionsData = this.ensureCaptionDataArrayHasLanguage(\n\t\tcaptionsData,\n\t\tthis.languageFallbackChain[ 0 ]\n\t);\n\n\tif ( this.canEdit ) {\n\t\tcaptionsData = this.addCaptionsDataForUserLanguages( captionsData );\n\t}\n\n\treturn {\n\t\tcaptionsData: captionsData,\n\t\torderedLanguageCodes: this.getOrderedLangCodes( captionsData )\n\t};\n};\n\n/**\n * If the input object does not have a CaptionData object for the specified langCode, add a\n * blank one\n *\n * @param {Object} captionDataArray Object with langCodes as keys and CaptionData objects as values\n * @param {string} langCode\n * @return {Object} Object with langCodes as keys and CaptionData objects as values\n * @private\n */\nCaptionsPanel.prototype.ensureCaptionDataArrayHasLanguage = function (\n\tcaptionDataArray,\n\tlangCode\n) {\n\tif ( captionDataArray[ langCode ] === undefined ) {\n\t\tcaptionDataArray[ langCode ] = new CaptionData( langCode );\n\t}\n\treturn captionDataArray;\n};\n\n/**\n * If the input object does not have a CaptionData object for each user language,\n * add a blank one\n *\n * @param {Object} captionDataArray Object with langCodes as keys and CaptionData objects as values\n * @return {Object} Object with langCodes as keys and CaptionData objects as values\n * @private\n */\nCaptionsPanel.prototype.addCaptionsDataForUserLanguages = function ( captionDataArray ) {\n\tvar self = this;\n\t// Create CaptionData objects for user languages that we don't already have on the screen\n\tthis.userLanguages.forEach( function ( langCode ) {\n\t\tcaptionDataArray = self.ensureCaptionDataArrayHasLanguage( captionDataArray, langCode );\n\t} );\n\treturn captionDataArray;\n};\n\n/**\n * Returns a list of langCodes from captionDataArray, ordered based on the rules specified\n * in the class comments\n *\n * @param {Object} captionDataArray Object with langCodes as keys and CaptionData objects as values\n * @return {Array}\n * @private\n */\nCaptionsPanel.prototype.getOrderedLangCodes = function ( captionDataArray ) {\n\tvar i,\n\t\tcaptionLanguages = Object.keys( captionDataArray ),\n\t\trearrangedCaptionLanguages = [];\n\n\t// First language in fallback chain (i.e. the interface language) is always first\n\trearrangedCaptionLanguages.push( this.languageFallbackChain[ 0 ] );\n\n\t// If there is no data for the interface language, then the first language in the fallback\n\t// chain with a value goes next\n\tif (\n\t\ttypeof captionDataArray[ this.languageFallbackChain[ 0 ] ] !== CaptionData ||\n\t\tcaptionDataArray[ this.languageFallbackChain[ 0 ] ].text !== ''\n\t) {\n\t\tfor ( i = 1; i < this.languageFallbackChain.length; i++ ) {\n\t\t\tif (\n\t\t\t\tcaptionDataArray[ this.languageFallbackChain[ i ] ] &&\n\t\t\t\tcaptionDataArray[ this.languageFallbackChain[ i ] ].text !== ''\n\t\t\t) {\n\t\t\t\trearrangedCaptionLanguages.push( this.languageFallbackChain[ i ] );\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// User languages go next\n\tthis.userLanguages.forEach( function ( langCode ) {\n\t\tif ( rearrangedCaptionLanguages.indexOf( langCode ) === -1 ) {\n\t\t\trearrangedCaptionLanguages.push( langCode );\n\t\t}\n\t} );\n\n\t// And finally all other languages\n\tcaptionLanguages.forEach( function ( langCode ) {\n\t\tif ( rearrangedCaptionLanguages.indexOf( langCode ) === -1 ) {\n\t\t\trearrangedCaptionLanguages.push( langCode );\n\t\t}\n\t} );\n\n\treturn rearrangedCaptionLanguages;\n};\n\n/**\n * @return {Object|jQuery.Promise<Object>}\n */\nCaptionsPanel.prototype.getTemplateData = function () {\n\tif ( this.state.editing === true ) {\n\t\treturn this.getTemplateDataEditable();\n\t} else {\n\t\treturn this.getTemplateDataReadOnly();\n\t}\n};\n\n/**\n * @return {Object|jQuery.Promise<Object>}\n */\nCaptionsPanel.prototype.getTemplateDataEditable = function () {\n\tvar self = this,\n\t\ttemplateCaptions = [],\n\t\tdata = {\n\t\t\tediting: true,\n\t\t\ttitle: mw.message( 'wikibasemediainfo-entitytermsforlanguagelistview-caption' ).text(),\n\t\t\teditActionsWidget: this.editActionsWidget\n\t\t},\n\t\tinputErrorFound = false;\n\n\tObject.keys( this.state.captionsDataEditors ).forEach( function ( guid ) {\n\t\tvar captionDataEditor = self.state.captionsDataEditors[ guid ];\n\n\t\ttemplateCaptions.push( {\n\t\t\tshow: true,\n\t\t\tempty: false,\n\t\t\ttextDirection: $.uls.data.getDir( captionDataEditor.getLanguageCode() ),\n\t\t\tlangCode: captionDataEditor.getLanguageCode(),\n\t\t\tlanguage: captionDataEditor.getLanguageSelector(),\n\t\t\tcaption: captionDataEditor.getTextInput(),\n\t\t\tdeleter: captionDataEditor.getDeleter(),\n\t\t\tinputError: captionDataEditor.getInputError(),\n\t\t\tinputWarning: captionDataEditor.getInputWarning()\n\t\t} );\n\n\t\tif ( captionDataEditor.getInputError() !== '' ) {\n\t\t\tinputErrorFound = true;\n\t\t}\n\t} );\n\tdata.captions = templateCaptions;\n\n\tif ( !this.hasChanges() ) {\n\t\tdata.editActionsWidget.disablePublish();\n\t} else {\n\t\tif ( inputErrorFound ) {\n\t\t\tdata.editActionsWidget.disablePublish();\n\t\t} else {\n\t\t\tdata.editActionsWidget.enablePublish();\n\t\t}\n\t}\n\n\treturn data;\n};\n\n/**\n * @return {Object|jQuery.Promise<Object>}\n */\nCaptionsPanel.prototype.getTemplateDataReadOnly = function () {\n\tvar self = this,\n\t\ttemplateCaptions = [],\n\t\tshowCaptionFlags = this.getShowCaptionFlagsByLangCode(),\n\t\tcount = 0,\n\t\tdata;\n\n\t// basic template data\n\tdata = {\n\t\tediting: false,\n\t\ttitle: mw.message( 'wikibasemediainfo-entitytermsforlanguagelistview-caption' ).text()\n\t};\n\n\t// the \"edit\" button\n\tif ( this.canEdit ) {\n\t\tdata.editToggle = new OO.ui.ButtonWidget( {\n\t\t\tlabel: mw.message( 'wikibasemediainfo-filepage-edit' ).text(),\n\t\t\tframed: false,\n\t\t\tflags: 'progressive',\n\t\t\ttitle: mw.message( 'wikibasemediainfo-filepage-edit-captions' ).text(),\n\t\t\tclasses: [ 'wbmi-entityview-editButton' ]\n\t\t} );\n\t\tdata.editToggle.connect( this, { click: [ 'makeEditable' ] } );\n\t} else {\n\t\tdata.editToggle = '';\n\t}\n\n\t// \"see X more languages\"/\"see fewer languages\" link\n\tif ( self.getHideableLanguageCount() > 0 ) {\n\t\tdata.languagesViewWidget = new OO.ui.ButtonWidget( {\n\t\t\ticon: this.state.displayAllLanguages ? 'collapse' : 'expand',\n\t\t\tflags: 'progressive',\n\t\t\tframed: false,\n\t\t\tlabel: this.state.displayAllLanguages ?\n\t\t\t\tmw.message( 'wikibasemediainfo-filepage-fewer-languages' ).text() :\n\t\t\t\tmw.message(\n\t\t\t\t\t'wikibasemediainfo-filepage-more-languages',\n\t\t\t\t\tself.getHideableLanguageCount()\n\t\t\t\t).text()\n\t\t} ).on(\n\t\t\t'click',\n\t\t\tthis.setState.bind( this, { displayAllLanguages: !this.state.displayAllLanguages } )\n\t\t);\n\t}\n\n\t// captions data\n\tthis.state.orderedLanguageCodes.forEach( function ( langCode ) {\n\t\tvar captionData = self.state.captionsData[ langCode ],\n\t\t\tlanguage,\n\t\t\tcaption;\n\n\t\tlanguage = captionData.languageText;\n\t\tcaption = captionData.text ?\n\t\t\tcaptionData.text : mw.message( 'wikibasemediainfo-filepage-caption-empty' ).text();\n\n\t\ttemplateCaptions.push( {\n\t\t\tshow: self.state.displayAllLanguages ? true : showCaptionFlags[ langCode ],\n\t\t\tindex: count,\n\t\t\ttextDirection: captionData.direction,\n\t\t\tlangCode: captionData.languageCode,\n\t\t\tlanguage: language,\n\t\t\tcaption: caption,\n\t\t\tempty: captionData.text === ''\n\t\t} );\n\t\tcount++;\n\n\t} );\n\tdata.captions = templateCaptions;\n\n\treturn data;\n};\n\n/**\n * Triggered when cancelling the edit mode.\n */\nCaptionsPanel.prototype.onCancel = function () {\n\tvar self = this;\n\n\tif ( this.hasChanges() ) {\n\t\tOO.ui.confirm(\n\t\t\tmw.msg( 'wikibasemediainfo-filepage-cancel-confirm' ),\n\t\t\t{\n\t\t\t\ttitle: mw.msg( 'wikibasemediainfo-filepage-cancel-confirm-title' ),\n\t\t\t\tactions: [\n\t\t\t\t\t{\n\t\t\t\t\t\taction: 'accept',\n\t\t\t\t\t\tlabel: mw.msg( 'wikibasemediainfo-filepage-cancel-confirm-accept' ),\n\t\t\t\t\t\tflags: [ 'primary', 'destructive' ]\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\taction: 'reject',\n\t\t\t\t\t\tlabel: mw.msg( 'ooui-dialog-message-reject' ),\n\t\t\t\t\t\tflags: 'safe'\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t).then( function ( confirmed ) {\n\t\t\tif ( confirmed ) {\n\t\t\t\tself.restoreToSaved();\n\t\t\t}\n\t\t} );\n\t} else {\n\t\tthis.restoreToSaved();\n\t}\n};\n\n/**\n * Recreate this.state.captionsDataEditors with one element removed, and update\n * this.state.captionsData\n *\n * @param {string} guidToRemove\n */\nCaptionsPanel.prototype.onCaptionDeleted = function ( guidToRemove ) {\n\tvar modifiedCaptionsDataEditors = $.extend( {}, this.state.captionsDataEditors );\n\tdelete modifiedCaptionsDataEditors[ guidToRemove ];\n\tthis.setState( {\n\t\tcaptionsDataEditors: modifiedCaptionsDataEditors\n\t} ).then( this.onDataChanged.bind( this ) );\n};\n\n/**\n * 1. Make sure each language can only be selected once\n * 2. Update the direction of textInputs based on selected languages\n * 3. Update this.state.captions from this.state.captionsDataEditors\n */\nCaptionsPanel.prototype.onDataChanged = function () {\n\tvar self = this, captionsData = {};\n\tthis.refreshLanguageSelectorsOptions();\n\tObject.keys( this.state.captionsDataEditors ).forEach( function ( guid ) {\n\t\tvar langSelector = self.state.captionsDataEditors[ guid ].languageSelector,\n\t\t\tlangCode = langSelector.getValue(),\n\t\t\ttextInput = self.state.captionsDataEditors[ guid ].textInput;\n\t\tif ( langCode ) {\n\t\t\ttextInput.setDir( $.uls.data.getDir( langCode ) );\n\t\t\tif ( textInput.getValue() ) {\n\t\t\t\tcaptionsData[ langCode ] = new CaptionData( langCode, textInput.getValue() );\n\t\t\t}\n\t\t}\n\t} );\n\tthis.setState( this.getCaptionsState( captionsData ) );\n};\n\n/**\n * Restores to a read-only view with the saved captions data\n */\nCaptionsPanel.prototype.restoreToSaved = function () {\n\tvar restoredState = $.extend( {}, this.getCaptionsState( this.savedCaptionsData ), {\n\t\tediting: false,\n\t\tdisabled: false\n\t} );\n\tthis.setState( restoredState );\n};\n\n/**\n * Gets all languages that we accept captions for EXCEPT languages in excludeLanguages\n *\n * @param {string[]} excludeLanguages Languages to exclude from the return array\n * @return {Object} Lang codes as keys, lang names in interface language as values\n * @private\n */\nCaptionsPanel.prototype.getAvailableLanguages = function ( excludeLanguages ) {\n\tvar languages = {};\n\t$.extend( languages, wbTermsLanguages );\n\t( excludeLanguages || [] ).forEach( function ( languageCode ) {\n\t\tdelete languages[ languageCode ];\n\t} );\n\treturn languages;\n};\n\n/**\n * Make sure available languages in each language selector don't include languages that are\n * selected in another selector\n *\n * @private\n */\nCaptionsPanel.prototype.refreshLanguageSelectorsOptions = function () {\n\tvar self = this,\n\t\tcurrentlySelectedLanguages = [],\n\t\tcaptionsDataEditors = this.state.captionsDataEditors;\n\n\tObject.keys( captionsDataEditors ).forEach( function ( guid ) {\n\t\tcurrentlySelectedLanguages.push( captionsDataEditors[ guid ].languageSelector.getValue() );\n\t} );\n\tObject.keys( captionsDataEditors ).forEach( function ( guid ) {\n\t\tcaptionsDataEditors[ guid ].languageSelector.updateLanguages(\n\t\t\tself.getAvailableLanguages(\n\t\t\t\tcurrentlySelectedLanguages.filter( function ( langCode ) {\n\t\t\t\t\treturn langCode !== captionsDataEditors[ guid ].languageSelector.getValue();\n\t\t\t\t} )\n\t\t\t)\n\t\t);\n\t} );\n};\n\n/**\n * @return {boolean} True if any captions have been changed/added/deleted\n */\nCaptionsPanel.prototype.hasChanges = function () {\n\tvar self = this,\n\t\tnonEmptyCaptionsData = {},\n\t\thasChanges;\n\thasChanges = Object.keys( this.state.captionsData ).some( function ( langCode ) {\n\t\tif ( self.state.captionsData[ langCode ].text !== '' ) {\n\t\t\tnonEmptyCaptionsData[ langCode ] = self.state.captionsData[ langCode ];\n\t\t\tif ( !self.savedCaptionsData[ langCode ] ) {\n\t\t\t\thasChanges = true;\n\t\t\t\treturn true;\n\t\t\t} else if (\n\t\t\t\tself.state.captionsData[ langCode ].text !== self.savedCaptionsData[ langCode ].text\n\t\t\t) {\n\t\t\t\thasChanges = true;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t} );\n\tif (\n\t\tObject.keys( nonEmptyCaptionsData ).length !==\n\t\tObject.keys( this.savedCaptionsData ).length\n\t) {\n\t\thasChanges = true;\n\t}\n\treturn hasChanges;\n};\n\n/**\n * Get a value object for sending data to the api\n *\n * @param {string} language Language code\n * @param {string} text Caption text\n * @return {{bot: number, action: string, id, value: *, language: *}} Value object\n * @private\n */\nCaptionsPanel.prototype.getWbSetLabelParams = function ( language, text ) {\n\tvar apiParams = {\n\t\t/*\n\t\t * Unconditionally set the bot parameter to match the UI behavior of core.\n\t\t * In normal page editing, if you have the \"bot\" user right and edit through the GUI\n\t\t * interface, your edit is marked as bot no matter what.\n\t\t * @see https://gerrit.wikimedia.org/r/71246\n\t\t * @see https://phabricator.wikimedia.org/T189477\n\t\t */\n\t\tbot: 1,\n\t\taction: 'wbsetlabel',\n\t\tid: mw.config.get( 'wbEntityId' ),\n\t\tvalue: text,\n\t\tlanguage: language\n\t};\n\t// Don't send baserevid unless we already have saved captions (a quirk of the api)\n\tif ( Object.keys( this.savedCaptionsData ).length > 0 ) {\n\t\tapiParams.baserevid = mw.mediaInfo.structuredData.currentRevision;\n\t}\n\treturn apiParams;\n};\n\n/**\n * Returns an object containing of showCaption flags for each element of labelsData, indexed by\n * langCode. Flags are set to true if a caption *must always* be shown\n *\n * See class comments for rules on when to show/hide captions\n *\n * @return {Object}\n * Array of showCaption flags, indexed by langCode\n * @private\n */\nCaptionsPanel.prototype.getShowCaptionFlagsByLangCode = function () {\n\tvar self = this,\n\t\tfirstCaptionIsBlank,\n\t\tindexedShowCaptionFlags = {};\n\n\tthis.state.orderedLanguageCodes.forEach( function ( langCode, index ) {\n\t\tvar captionData = self.state.captionsData[ langCode ],\n\t\t\tshowCaption;\n\t\tif ( index === 0 ) {\n\t\t\tshowCaption = true;\n\t\t\tfirstCaptionIsBlank = ( captionData.text === '' );\n\t\t} else if (\n\t\t\tindex === 1 &&\n\t\t\tfirstCaptionIsBlank &&\n\t\t\tcaptionData.text !== ''\n\t\t) {\n\t\t\tshowCaption = true;\n\t\t} else {\n\t\t\tif ( self.userLanguages.indexOf( langCode ) === -1 ) {\n\t\t\t\tshowCaption = false;\n\t\t\t} else {\n\t\t\t\tshowCaption = true;\n\t\t\t}\n\t\t}\n\t\tindexedShowCaptionFlags[ langCode ] = showCaption;\n\t} );\n\treturn indexedShowCaptionFlags;\n};\n\n/**\n * Returns the number of languages which may be hidden from the user\n *\n * See class comments for rules on when to show/hide captions\n *\n * @return {number}\n * @private\n */\nCaptionsPanel.prototype.getHideableLanguageCount = function () {\n\tvar showCaptionFlags = this.getShowCaptionFlagsByLangCode();\n\n\treturn Object.keys( showCaptionFlags ).filter(\n\t\tfunction ( langCode ) {\n\t\t\treturn showCaptionFlags[ langCode ] === false;\n\t\t}\n\t).length;\n};\n\nCaptionsPanel.prototype.makeEditable = function () {\n\tvar self = this;\n\n\t// Show IP address logging notice to anon users\n\tif ( mw.user.isAnon() ) {\n\t\tAnonWarning.notifyOnce();\n\t}\n\n\t// show dialog informing user of licensing\n\tself.licenseDialogWidget.getConfirmationIfNecessary().then( function () {\n\t\tvar entityId = mw.config.get( 'wbEntityId' );\n\t\tself.pushPending();\n\t\t// refresh caption data from the api\n\t\tself.api\n\t\t\t.get( {\n\t\t\t\taction: 'wbgetentities',\n\t\t\t\tprops: 'info|labels',\n\t\t\t\tids: entityId\n\t\t\t} )\n\t\t\t.done( function ( result ) {\n\t\t\t\tmw.mediaInfo.structuredData.currentRevision = result.entities[ entityId ].lastrevid;\n\t\t\t\tself.savedCaptionsData = self.captionsDataFromMediaInfoEntity(\n\t\t\t\t\tresult.entities[ entityId ]\n\t\t\t\t);\n\t\t\t} )\n\t\t\t.fail( function () {\n\t\t\t\t// Ignore the failure and just make do with the data we already have saved\n\t\t\t} )\n\t\t\t.always( function () {\n\t\t\t\tvar captionsDataEditors = {},\n\t\t\t\t\tcaptionsState = self.getCaptionsState(\n\t\t\t\t\t\t// Copy by value so the saved data isn't modified\n\t\t\t\t\t\t$.extend( {}, self.savedCaptionsData )\n\t\t\t\t\t);\n\n\t\t\t\tcaptionsState.orderedLanguageCodes.forEach( function ( langCode ) {\n\t\t\t\t\tvar guid = self.createGuid();\n\t\t\t\t\tcaptionsDataEditors[ guid ] = self.createCaptionDataEditor(\n\t\t\t\t\t\tguid,\n\t\t\t\t\t\tself.savedCaptionsData[ langCode ] || new CaptionData( langCode )\n\t\t\t\t\t);\n\t\t\t\t} );\n\n\t\t\t\tself.setState(\n\t\t\t\t\t$.extend(\n\t\t\t\t\t\tcaptionsState,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcaptionsDataEditors: captionsDataEditors,\n\t\t\t\t\t\t\tediting: true\n\t\t\t\t\t\t}\n\t\t\t\t\t)\n\t\t\t\t).then( function () {\n\t\t\t\t\tself.refreshLanguageSelectorsOptions();\n\t\t\t\t\tself.popPending();\n\t\t\t\t} );\n\t\t\t} );\n\t} );\n};\n\nCaptionsPanel.prototype.addNewEmptyLanguageRow = function () {\n\tvar guid = this.createGuid(),\n\t\tcaptionsDataEditors = $.extend(\n\t\t\t{},\n\t\t\tthis.state.captionsDataEditors\n\t\t);\n\tcaptionsDataEditors[ guid ] = this.createCaptionDataEditor( guid, new CaptionData() );\n\tthis.setState( {\n\t\tcaptionsDataEditors: captionsDataEditors\n\t} ).then( this.refreshLanguageSelectorsOptions.bind( this ) );\n};\n\nCaptionsPanel.prototype.sendData = function () {\n\tvar self = this,\n\t\tcaptionsDataEditors = $.extend( {}, this.state.captionsDataEditors ),\n\t\tpromise = $.Deferred().resolve().promise();\n\n\tthis.setSending();\n\n\t// Send changed data\n\tObject.keys( captionsDataEditors ).forEach( function ( guid ) {\n\t\tvar captionDataEditor = captionsDataEditors[ guid ],\n\t\t\tlangCode = captionDataEditor.getLanguageCode(),\n\t\t\ttext = captionDataEditor.getText(),\n\t\t\tsavedData = self.savedCaptionsData[ langCode ];\n\n\t\tif ( text && langCode && ( !savedData || savedData.text !== text ) ) {\n\t\t\tpromise = promise.then( function () {\n\t\t\t\treturn self.api.postWithToken(\n\t\t\t\t\t'csrf',\n\t\t\t\t\tself.getWbSetLabelParams( langCode, text )\n\t\t\t\t)\n\t\t\t\t\t.done( function ( result ) {\n\t\t\t\t\t\tmw.mediaInfo.structuredData.currentRevision = result.entity.lastrevid;\n\t\t\t\t\t\tself.savedCaptionsData[ langCode ] =\n\t\t\t\t\t\t\tnew CaptionData( langCode, text );\n\t\t\t\t\t} )\n\t\t\t\t\t.fail( function ( errorCode, error ) {\n\t\t\t\t\t\tvar apiError =\n\t\t\t\t\t\t\twikibase.api.RepoApiError.newFromApiResponse( error, 'save' );\n\t\t\t\t\t\tcaptionDataEditor.setInputError( apiError.detailedMessage );\n\t\t\t\t\t} );\n\t\t\t} );\n\t\t}\n\t} );\n\n\t// Delete removed data\n\tObject.keys( this.savedCaptionsData ).forEach( function ( langCode ) {\n\t\tvar captionsData = self.state.captionsData[ langCode ];\n\t\tif ( !captionsData || captionsData.text === '' ) {\n\t\t\tpromise = promise.then( function () {\n\t\t\t\treturn self.api.postWithToken(\n\t\t\t\t\t'csrf',\n\t\t\t\t\tself.getWbSetLabelParams( langCode, '' )\n\t\t\t\t)\n\t\t\t\t\t.done( function ( result ) {\n\t\t\t\t\t\tmw.mediaInfo.structuredData.currentRevision = result.entity.lastrevid;\n\t\t\t\t\t\tdelete self.savedCaptionsData[ langCode ];\n\t\t\t\t\t} )\n\t\t\t\t\t.fail( function ( errorCode, error ) {\n\t\t\t\t\t\tvar apiError =\n\t\t\t\t\t\t\t\twikibase.api.RepoApiError.newFromApiResponse( error, 'save' ),\n\t\t\t\t\t\t\tguid = self.createGuid(),\n\t\t\t\t\t\t\tcaptionDataEditor = self.createCaptionDataEditor( guid, self.savedCaptionsData[ langCode ] );\n\t\t\t\t\t\tcaptionDataEditor.setInputError( apiError.detailedMessage );\n\t\t\t\t\t\tcaptionsDataEditors[ guid ] = captionDataEditor;\n\t\t\t\t\t} );\n\t\t\t} );\n\t\t}\n\t} );\n\n\tpromise.then( function () {\n\t\tself.setState(\n\t\t\t$.extend(\n\t\t\t\t{},\n\t\t\t\tself.getCaptionsState( self.savedCaptionsData ),\n\t\t\t\t{\n\t\t\t\t\tediting: false,\n\t\t\t\t\tcaptionsDataEditors: {},\n\t\t\t\t\tdisplayAllLanguages: true\n\t\t\t\t}\n\t\t\t)\n\t\t);\n\t} ).catch( function () {\n\t\tself.setState( {\n\t\t\tediting: true,\n\t\t\tcaptionsDataEditors: captionsDataEditors\n\t\t} );\n\t} ).always( function () {\n\t\tself.setReady();\n\t} );\n};\n\n/**\n * @param {string} guid\n * @param {CaptionData} captionData\n * @return {CaptionDataEditor}\n */\nCaptionsPanel.prototype.createCaptionDataEditor = function ( guid, captionData ) {\n\tvar captionDataEditor = new CaptionDataEditor( guid, captionData, { warnWithinMaxCaptionLength: this.warnWithinMaxCaptionLength } );\n\tthis.enableCaptionDataEditor( captionDataEditor );\n\treturn captionDataEditor;\n};\n\n/**\n * @param {CaptionDataEditor} captionDataEditor\n */\nCaptionsPanel.prototype.enableCaptionDataEditor = function ( captionDataEditor ) {\n\tcaptionDataEditor.setDisabled( false );\n\n\tcaptionDataEditor.connect( this, { captionDeleted: 'onCaptionDeleted' } );\n\tcaptionDataEditor.connect( this, { languageSelectorUpdated: 'onDataChanged' } );\n\tcaptionDataEditor.connect( this, { textInputChanged: 'onDataChanged' } );\n\tcaptionDataEditor.connect( this, { textInputSubmitted: 'sendData' } );\n};\n\n/**\n * @param {CaptionDataEditor} captionDataEditor\n */\nCaptionsPanel.prototype.disableCaptionDataEditor = function ( captionDataEditor ) {\n\tcaptionDataEditor.setDisabled( true );\n\n\tcaptionDataEditor.disconnect( this, { captionDeleted: 'onCaptionDeleted' } );\n\tcaptionDataEditor.disconnect( this, { languageSelectorUpdated: 'onDataChanged' } );\n\tcaptionDataEditor.disconnect( this, { textInputChanged: 'onDataChanged' } );\n\tcaptionDataEditor.disconnect( this, { textInputSubmitted: 'sendData' } );\n};\n\n/**\n * Puts the panel into a 'sending' state without re-rendering\n */\nCaptionsPanel.prototype.setSending = function () {\n\tvar self = this;\n\tthis.editActionsWidget.setStateSending();\n\tObject.keys( this.state.captionsDataEditors ).forEach( function ( guid ) {\n\t\tself.disableCaptionDataEditor( self.state.captionsDataEditors[ guid ] );\n\t} );\n\tthis.pushPending();\n};\n\n/**\n * Puts the panel into a ready' state without re-rendering\n */\nCaptionsPanel.prototype.setReady = function () {\n\tvar self = this;\n\tthis.editActionsWidget.setStateReady();\n\tObject.keys( this.state.captionsDataEditors ).forEach( function ( guid ) {\n\t\tself.enableCaptionDataEditor( self.state.captionsDataEditors[ guid ] );\n\t} );\n\tthis.popPending();\n};\n\n/**\n * Create a GUID for keeping track of CaptionDataEditWidgets\n *\n * @see derived from https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript\n * @return {string}\n */\nCaptionsPanel.prototype.createGuid = function () {\n\treturn 'xxxxxxxx-xxxx-4xxx-8xxx-xxxxxxxxxxxx'.replace(\n\t\t/[x]/g, function () {\n\t\t\treturn ( Math.random() * 16 ).toString( 16 ).substring( 0, 1 );\n\t\t}\n\t);\n};\n\nCaptionsPanel.prototype.isEditable = function () {\n\treturn this.state.editable;\n};\n\nCaptionsPanel.prototype.isDisabled = function () {\n\treturn false;\n};\n\nmodule.exports = CaptionsPanel;\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/filepage/LicenseDialogWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/filepage/ProtectionMsgWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/filepage/StatementPanel.js","messages":[{"ruleId":"jsdoc/check-tag-names","severity":1,"message":"Invalid JSDoc tag name \"mixins\".","line":18,"column":null,"nodeType":"Block","endLine":18,"endColumn":null}],"errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use strict';\n\n/**\n * @external datamodel\n */\n\nvar AnonWarning = require( './AnonWarning.js' ),\n\tFormatValueElement = require( 'wikibase.mediainfo.base' ).FormatValueElement,\n\tLicenseDialogWidget = require( './LicenseDialogWidget.js' ),\n\tStatementWidget = require( 'wikibase.mediainfo.statements' ).StatementWidget,\n\tdataTypesMap = mw.config.get( 'wbDataTypes' ),\n\tStatementPanel;\n\n/**\n * Panel for displaying/editing structured data statements\n *\n * @extends OO.ui.Element\n * @mixins OO.ui.mixin.PendingElement\n *\n * @constructor\n * @param {Object} config Configuration options\n * @param {jQuery} config.$element Node to replace with statement panel\n * @param {string} config.entityId Entity ID (e.g. M123 id of the file you just uploaded)\n * @param {string} config.propertyId Property ID (e.g. P123 id of `depicts` property)\n * @param {string} config.propertyType Property datatype (e.g. 'wikibase-item', 'url', 'string', ...)\n * @param {Object} [config.helpUrls]  An object with property id as members and help urls for\n *  the property as values\n *  e.g. { P1: \"https://commons.wikimedia.org/wiki/Special:MyLanguage/Commons:Depicts\" }\n * @fires dataLoadedReadOnly\n */\nStatementPanel = function StatementPanelConstructor( config ) {\n\t// Parent constructor\n\tStatementPanel.super.apply( this, arguments );\n\n\tthis.$element = config.$element;\n\tdelete config.$element;\n\tthis.config = config || {};\n\n\t// Mixin constructors\n\tOO.ui.mixin.PendingElement.call( this, this.config );\n\n\tif ( this.$element.attr( 'data-formatvalue' ) ) {\n\t\tthis.populateFormatValueCache( JSON.parse( this.$element.attr( 'data-formatvalue' ) || '{}' ) );\n\t}\n\n\tthis.licenseDialogWidget = new LicenseDialogWidget();\n\n\tthis.statementWidget = new StatementWidget( $.extend( {\n\t\tshowControls: true,\n\t\tvalueType: this.config.propertyType in dataTypesMap ? dataTypesMap[ this.config.propertyType ].dataValueType : undefined\n\t}, this.config ) );\n\n\tthis.bindEventHandlers();\n\n\t// attach the widget to DOM, replacing the server-side rendered equivalent\n\tthis.$element.empty().append( this.statementWidget.$element );\n\n\tif ( !this.isSupportedType() ) {\n\t\tthis.$element.addClass( 'wbmi-entityview-statementsGroup-unsupported' );\n\t}\n\n\tthis.$pending = this.$element;\n};\n\n/* Inheritance */\nOO.inheritClass( StatementPanel, OO.ui.Widget );\nOO.mixinClass( StatementPanel, OO.ui.mixin.PendingElement );\n\nStatementPanel.prototype.bindEventHandlers = function () {\n\tthis.statementWidget.connect( this, { cancel: 'makeReadOnly' } );\n\tthis.statementWidget.connect( this, { publish: 'sendData' } );\n\tthis.statementWidget.connect( this, { edit: 'makeEditable' } ); // clicked 'edit'\n\tthis.statementWidget.connect( this, { change: 'makeEditable' } ); // changed otherwise (e.g. 'make prominent')\n\tthis.statementWidget.connect( this, { widgetRemoved: 'remove' } );\n};\n\n/**\n * @param {datamodel.StatementList} data\n * @return {jQuery.promise}\n */\nStatementPanel.prototype.setData = function ( data ) {\n\tthis.unbindEventHandlers();\n\n\t// don't subscribe to events until statementwidget has been populated with data\n\treturn this.statementWidget.resetData( data ).then( this.bindEventHandlers.bind( this ) );\n};\n\nStatementPanel.prototype.unbindEventHandlers = function () {\n\tthis.statementWidget.disconnect( this, { cancel: 'makeReadOnly' } );\n\tthis.statementWidget.disconnect( this, { publish: 'sendData' } );\n\tthis.statementWidget.disconnect( this, { edit: 'makeEditable' } );\n\tthis.statementWidget.disconnect( this, { change: 'makeEditable' } );\n\tthis.statementWidget.disconnect( this, { widgetRemoved: 'remove' } );\n};\n\n/**\n * Pre-populate the formatValue cache, which will save some\n * API calls if we end up wanting to format some of these...\n *\n * @param {Object} data\n */\nStatementPanel.prototype.populateFormatValueCache = function ( data ) {\n\tObject.keys( data ).forEach( function ( dataValue ) {\n\t\tObject.keys( data[ dataValue ] ).forEach( function ( format ) {\n\t\t\tObject.keys( data[ dataValue ][ format ] ).forEach( function ( language ) {\n\t\t\t\tvar properties = data[ dataValue ][ format ][ language ];\n\t\t\t\tObject.keys( properties ).forEach( function ( propertyId ) {\n\t\t\t\t\tvar json = JSON.parse( dataValue ),\n\t\t\t\t\t\tkey = FormatValueElement.getKey(\n\t\t\t\t\t\t\tdataValues.newDataValue( json.type, json.value ),\n\t\t\t\t\t\t\tformat,\n\t\t\t\t\t\t\tlanguage,\n\t\t\t\t\t\t\tpropertyId || undefined\n\t\t\t\t\t\t),\n\t\t\t\t\t\tresult = properties[ propertyId ];\n\t\t\t\t\tFormatValueElement.toCache( key, result );\n\t\t\t\t} );\n\t\t\t} );\n\t\t} );\n\t} );\n};\n\n/**\n * Check for changes to statement claims or number of statements\n *\n * @return {boolean}\n */\nStatementPanel.prototype.hasChanges = function () {\n\treturn this.statementWidget.hasChanges();\n};\n\n/**\n * @return {boolean}\n */\nStatementPanel.prototype.isEditable = function () {\n\treturn this.statementWidget.isEditing();\n};\n\n/**\n * @return {boolean}\n */\nStatementPanel.prototype.isSupportedType = function () {\n\tvar supportedTypes = mw.config.get( 'wbmiSupportedDataTypes' ) || [];\n\treturn supportedTypes.indexOf( this.config.propertyType ) >= 0;\n};\n\n/**\n * Toggle the panel into edit mode. This method is asynchronous.\n */\nStatementPanel.prototype.makeEditable = function () {\n\tvar self = this;\n\n\t// Show IP address logging notice to anon users\n\tif ( mw.user.isAnon() ) {\n\t\tAnonWarning.notifyOnce();\n\t}\n\n\t// show dialog informing user of licensing & store the returned promise\n\t// in licenseAcceptance - submit won't be possible until dialog is closed\n\tthis.licenseDialogWidget.getConfirmationIfNecessary().then(\n\t\tfunction () {\n\t\t\tself.statementWidget.setEditing.bind( self.statementWidget, true );\n\n\t\t\tif ( !self.isSupportedType() ) {\n\t\t\t\tself.showUnsupportedPopup();\n\t\t\t}\n\t\t},\n\t\tthis.makeReadOnly.bind( this )\n\t);\n};\n\n/**\n * Toggle the panel into read mode. This method is asynchronous.\n */\nStatementPanel.prototype.makeReadOnly = function () {\n\tvar self = this;\n\tthis.statementWidget.disconnect( this, { change: 'makeEditable' } );\n\tthis.statementWidget.resetData().then( function () {\n\t\tself.statementWidget.connect( self, { change: 'makeEditable' } );\n\t\tself.emit( 'readOnly' );\n\t} );\n};\n\nStatementPanel.prototype.sendData = function () {\n\tvar self = this;\n\n\tthis.statementWidget.disconnect( this, { change: 'makeEditable' } );\n\tthis.pushPending();\n\n\tthis.statementWidget.submit( mw.mediaInfo.structuredData.currentRevision || undefined )\n\t\t.then( function ( response ) {\n\t\t\tmw.mediaInfo.structuredData.currentRevision = response.pageinfo.lastrevid;\n\t\t\tself.makeReadOnly();\n\n\t\t\t// if the statement widget is removed then also remove the panel\n\t\t\tif ( self.statementWidget.getData().length === 0 && !self.config.isDefaultProperty ) {\n\t\t\t\tself.remove();\n\t\t\t}\n\t\t} ).catch( function () {\n\t\t\t// allow panel to be re-enabled to allow trying submission again\n\t\t\tself.statementWidget.setDisabled( false );\n\t\t} ).always( function () {\n\t\t\tself.statementWidget.connect( self, { change: 'makeEditable' } );\n\t\t\tself.popPending();\n\t\t} );\n};\n\nStatementPanel.prototype.showUnsupportedPopup = function () {\n\tvar popup, popupMsg, $content;\n\n\tpopupMsg = mw.message(\n\t\t'wikibasemediainfo-statements-unsupported-property-type-content'\n\t).parse();\n\n\t$content = $( '<div>' ).append(\n\t\t$( '<h4>' ).html(\n\t\t\tmw.message( 'wikibasemediainfo-statements-unsupported-property-title' ).parse()\n\t\t),\n\t\t$( '<p>' ).html( popupMsg )\n\t);\n\n\tpopup = new OO.ui.PopupWidget( {\n\t\t$floatableContainer: this.statementWidget.$element,\n\t\tposition: 'after',\n\t\tpadded: true,\n\t\tautoClose: true,\n\t\t$content: $content\n\t} );\n\n\tthis.$element.append( popup.$element );\n\tpopup.toggle( true );\n};\n\n/**\n * Notifies the top-level Filepage/UploadWizard JS of removal so that it can be\n * handled properly.\n *\n * @fires widgetRemoved\n */\nStatementPanel.prototype.remove = function () {\n\tthis.emit( 'widgetRemoved', this.config.propertyId );\n};\n\n/**\n * Handle the response from a wbcheckconstraints api call\n *\n * @param {Object} response\n */\nStatementPanel.prototype.handleConstraintsResponse = function ( response ) {\n\tthis.statementWidget.handleConstraintsResponse(\n\t\tthis.extractResultsForPropertyId( response )\n\t);\n};\n\n/**\n * Extract the constraint check results for a certain statement from the full API response.\n *\n * @param {Object} response The constraint check results\n * @return {Object|null} An object containing lists of constraint check results,\n * or null if the results could not be extracted.\n * @see WikibaseQualityConstraints/modules/gadget.js::_extractResultsForStatement()\n */\nStatementPanel.prototype.extractResultsForPropertyId = function ( response ) {\n\tvar propertyId = this.config.propertyId,\n\t\tentityId = mw.config.get( 'wbEntityId' ),\n\t\tentityData = response.wbcheckconstraints[ entityId ];\n\tif ( 'claims' in entityData ) {\n\t\t// API v2 format\n\t\treturn entityData.claims[ propertyId ];\n\t} else {\n\t\treturn null;\n\t}\n};\n\nmodule.exports = StatementPanel;\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/filepage/init.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/getDeserializer.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/App.vue","messages":[{"ruleId":"compat/compat","severity":2,"message":"IntersectionObserverEntry is not supported in Safari 5.1, IE 11","line":184,"column":28,"nodeType":"MemberExpression","endLine":184,"endColumn":60}],"errorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div id=\"app\">\n\t\t<wbmi-autocomplete-search-input\n\t\t\tclass=\"wbmi-media-search-input\"\n\t\t\tname=\"wbmi-media-search-input\"\n\t\t\t:label=\"$i18n( 'wikibasemediainfo-special-mediasearch-input-label' )\"\n\t\t\t:initial-value=\"term\"\n\t\t\t:placeholder=\"$i18n( 'wikibasemediainfo-special-mediasearch-input-placeholder' )\"\n\t\t\t:clear-title=\"$i18n( 'wikibasemediainfo-special-mediasearch-clear-title' )\"\n\t\t\t:button-label=\"$i18n( 'searchbutton' )\"\n\t\t\t:lookup-results=\"lookupResults\"\n\t\t\t@input=\"getDebouncedLookupResults\"\n\t\t\t@submit=\"onUpdateTerm\"\n\t\t\t@clear=\"onClear\"\n\t\t\t@clear-lookup-results=\"clearLookupResults\"\n\t\t>\n\t\t</wbmi-autocomplete-search-input>\n\n\t\t<!-- Generate a tab for each key in the \"results\" object. Data types,\n\t\tmessages, and loading behavior are bound to this key. -->\n\t\t<wbmi-tabs :active=\"currentTab\" @tab-change=\"onTabChange\">\n\t\t\t<wbmi-tab v-for=\"tab in tabs\"\n\t\t\t\t:key=\"tab\"\n\t\t\t\t:name=\"tab\"\n\t\t\t\t:title=\"tabNames[ tab ]\">\n\t\t\t\t<!-- Display search filters for each tab. -->\n\t\t\t\t<search-filters\n\t\t\t\t\t:media-type=\"tab\"\n\t\t\t\t\t@filter-change=\"onFilterChange\"\n\t\t\t\t>\n\t\t\t\t</search-filters>\n\n\t\t\t\t<did-you-mean></did-you-mean>\n\n\t\t\t\t<transition-group\n\t\t\t\t\tname=\"wbmi-concept-chips-transition\"\n\t\t\t\t\tclass=\"wbmi-concept-chips-transition\"\n\t\t\t\t\ttag=\"div\"\n\t\t\t\t>\n\t\t\t\t\t<concept-chips\n\t\t\t\t\t\tv-if=\"enableConceptChips && tab === 'bitmap' && relatedConcepts.length > 0\"\n\t\t\t\t\t\t:key=\"'concept-chips-' + tab\"\n\t\t\t\t\t\t:media-type=\"tab\"\n\t\t\t\t\t\t:concepts=\"relatedConcepts\"\n\t\t\t\t\t\t@concept-select=\"onUpdateTerm\">\n\t\t\t\t\t</concept-chips>\n\n\t\t\t\t\t<div :key=\"'tab-content-' + tab\">\n\t\t\t\t\t\t<!-- Display the available results for each tab -->\n\t\t\t\t\t\t<search-results\n\t\t\t\t\t\t\t:ref=\"tab\"\n\t\t\t\t\t\t\t:media-type=\"tab\"\n\t\t\t\t\t\t\t@load-more=\"resetCountAndLoadMore( tab )\"\n\t\t\t\t\t\t></search-results>\n\t\t\t\t\t</div>\n\t\t\t\t</transition-group>\n\n\t\t\t\t<!-- Auto-load more results when user scrolls to the end of the list/grid,\n\t\t\t\tas long as the \"autoload counter\" for the tab has not reached zero -->\n\t\t\t\t<observer\n\t\t\t\t\tv-if=\"autoloadCounter[ tab ] > 0 && supportsObserver\"\n\t\t\t\t\t@intersect=\"getMoreResultsForTabIfAvailable( tab )\">\n\t\t\t\t</observer>\n\t\t\t</wbmi-tab>\n\t\t</wbmi-tabs>\n\t</div>\n</template>\n\n<script>\n/**\n * @file App.vue\n *\n * Top-level component for the Special:MediaSearch JS UI.\n * Contains two major elements:\n * - autocomplete search input\n * - tabs to display search results (one for each media type)\n *\n * Search query and search result data lives in Vuex, but this component\n * responds to user interactions like changes in query or tab, dispatches\n * Vuex actions to make API requests, and ensures that the URL parameters\n * remain in sync with the current search term and active tab (this is done\n * using history.replaceState)\n *\n * Autocomplete lookups are handled by a mixin. When new search input is\n * emitted from the AutocompleteSearchInput, the mixin handles the lookup\n * request and this component passes an array of string lookup results to the\n * AutocompleteSearchInput for display.\n */\nvar AUTOLOAD_COUNT = 2,\n\tmapState = require( 'vuex' ).mapState,\n\tmapGetters = require( 'vuex' ).mapGetters,\n\tmapMutations = require( 'vuex' ).mapMutations,\n\tmapActions = require( 'vuex' ).mapActions,\n\tWbmiAutocompleteSearchInput = require( './base/AutocompleteSearchInput.vue' ),\n\tWbmiTab = require( './base/Tab.vue' ),\n\tWbmiTabs = require( './base/Tabs.vue' ),\n\tSearchResults = require( './SearchResults.vue' ),\n\tSearchFilters = require( './SearchFilters.vue' ),\n\tConceptChips = require( './ConceptChips.vue' ),\n\tDidYouMean = require( './DidYouMean.vue' ),\n\tObserver = require( './base/Observer.vue' ),\n\tautocompleteLookupHandler = require( './../mixins/autocompleteLookupHandler.js' ),\n\tsearchOptions = require( './../data/searchOptions.json' ),\n\turl = new mw.Uri();\n\n// @vue/component\nmodule.exports = {\n\tname: 'MediaSearch',\n\n\tcomponents: {\n\t\t'wbmi-tabs': WbmiTabs,\n\t\t'wbmi-tab': WbmiTab,\n\t\t'wbmi-autocomplete-search-input': WbmiAutocompleteSearchInput,\n\t\t'search-results': SearchResults,\n\t\t'search-filters': SearchFilters,\n\t\t'concept-chips': ConceptChips,\n\t\t'did-you-mean': DidYouMean,\n\t\tobserver: Observer\n\t},\n\n\tmixins: [ autocompleteLookupHandler ],\n\n\tdata: function () {\n\t\treturn {\n\t\t\tcurrentTab: url.query.type || '',\n\n\t\t\t// Object with keys corresponding to each tab;\n\t\t\t// values are integers; set in the created() hook\n\t\t\tautoloadCounter: {},\n\n\t\t\t// Temporary feature flag for Concept Chips. To enable, add\n\t\t\t// ?conceptchips=true to the URL.\n\t\t\tenableConceptChips: !!url.query.conceptchips\n\t\t};\n\t},\n\n\tcomputed: $.extend( {}, mapState( [\n\t\t'term',\n\t\t'results',\n\t\t'pending',\n\t\t'relatedConcepts',\n\t\t'filterValues'\n\t] ), mapGetters( [\n\t\t'checkForMore',\n\t\t'allActiveFilters'\n\t] ), {\n\n\t\t/**\n\t\t * The names here need to match the keys found in this.results,\n\t\t * (which originate in Vuex store), but the order here matters\n\t\t * for visual presentation so they have been manually sorted.\n\t\t *\n\t\t * @return {string[]} [ 'bitmap', 'video', 'audio', 'page', 'other' ]\n\t\t */\n\t\ttabs: function () {\n\t\t\treturn [\n\t\t\t\t'bitmap',\n\t\t\t\t'audio',\n\t\t\t\t'video',\n\t\t\t\t'other',\n\t\t\t\t'page'\n\t\t\t];\n\t\t},\n\n\t\t/**\n\t\t * @return {Object} { bitmap: 'Images', video: 'Video', page: 'Categories and Pages'... }\n\t\t */\n\t\ttabNames: function () {\n\t\t\tvar names = {},\n\t\t\t\tprefix = 'wikibasemediainfo-special-mediasearch-tab-';\n\n\t\t\t// Get the i18n message for each tab title and assign to appropriate\n\t\t\t// key in returned object\n\t\t\tthis.tabs.forEach( function ( tab ) {\n\t\t\t\tnames[ tab ] = this.$i18n( prefix + tab ).text();\n\t\t\t}.bind( this ) );\n\n\t\t\treturn names;\n\t\t},\n\n\t\tsupportsObserver: function () {\n\t\t\treturn 'IntersectionObserver' in window &&\n\t\t\t\t'IntersectionObserverEntry' in window &&\n\t\t\t\t'intersectionRatio' in window.IntersectionObserverEntry.prototype;\n\t\t}\n\t} ),\n\n\tmethods: $.extend( {}, mapMutations( [\n\t\t'resetFilters',\n\t\t'resetResults',\n\t\t'clearRelatedConcepts',\n\t\t'clearDidYouMean',\n\t\t'setTerm',\n\t\t'setPending',\n\t\t'resetFilters',\n\t\t'addFilterValue'\n\t] ), mapActions( [\n\t\t'search',\n\t\t'getRelatedConcepts',\n\t\t'clear'\n\t] ), {\n\t\t/**\n\t\t * Keep UI state, URL, and history in sync as the user changes tabs.\n\t\t * Filter and sort preferences are tab-specific, so they need to be\n\t\t * re-created every time the current tab changes.\n\t\t *\n\t\t * @param {Object} newTab\n\t\t * @param {string} newTab.name\n\t\t */\n\t\tonTabChange: function ( newTab ) {\n\t\t\tthis.currentTab = newTab.name;\n\t\t\turl.query.type = newTab.name;\n\n\t\t\t// Record any currently active filters for the given tab in the URL\n\t\t\t// query params\n\t\t\tthis.clearFilterQueryParams();\n\t\t\tObject.keys( this.filterValues[ this.currentTab ] ).forEach( function ( filter ) {\n\t\t\t\turl.query[ filter ] = this.filterValues[ this.currentTab ][ filter ];\n\t\t\t}.bind( this ) );\n\n\t\t\t// Extract the complete set of query params as a new entry in the\n\t\t\t// history stack\n\t\t\twindow.history.pushState( url.query, null, '?' + url.getQueryString() );\n\n\t\t\t/* eslint-disable camelcase */\n\t\t\tthis.$log( {\n\t\t\t\taction: 'tab_change',\n\t\t\t\tsearch_query: this.term,\n\t\t\t\tsearch_media_type: this.currentTab\n\t\t\t} );\n\t\t\t/* eslint-enable camelcase */\n\t\t},\n\n\t\t/**\n\t\t * @param {Object} data\n\t\t * @param {string} data.mediaType\n\t\t * @param {string} data.filterType\n\t\t * @param {string} [data.value]\n\t\t */\n\t\tonFilterChange: function ( data ) {\n\t\t\t// the new search (with updated filter params) is handled\n\t\t\t// by the allActiveFilters watcher\n\n\t\t\tthis.$refs[ data.mediaType ][ 0 ].hideDetails();\n\n\t\t\tif ( data.value ) {\n\t\t\t\turl.query[ data.filterType ] = data.value;\n\t\t\t} else {\n\t\t\t\tdelete url.query[ data.filterType ];\n\t\t\t}\n\n\t\t\twindow.history.pushState( url.query, null, '?' + url.getQueryString() );\n\t\t},\n\n\t\t/**\n\t\t * Keep the UI state, URL, and history in sync as the user changes\n\t\t * search queries\n\t\t *\n\t\t * @param {string} newTerm\n\t\t */\n\t\tonUpdateTerm: function ( newTerm ) {\n\t\t\tthis.setTerm( newTerm );\n\t\t\turl.query.q = newTerm;\n\t\t\twindow.history.pushState( url.query, null, '?' + url.getQueryString() );\n\t\t},\n\n\t\t/**\n\t\t * Dispatch Vuex actions to clear existing term and results whenever a\n\t\t * \"clear\" event is detected. Update the URL and history as well.\n\t\t * Also resets the autoload counter to clear any \"load more\" buttons\n\t\t * that may have previously been visible in any of the tabs.\n\t\t */\n\t\tonClear: function () {\n\t\t\tthis.clear( this.currentTab );\n\t\t\tthis.clearLookupResults();\n\t\t\tthis.setPending( { type: this.currentTab, pending: false } );\n\n\t\t\turl.query.q = '';\n\t\t\tthis.clearFilterQueryParams();\n\t\t\twindow.history.pushState( url.query, null, '?' + url.getQueryString() );\n\t\t\tthis.autoloadCounter = this.setInitialAutoloadCountForTabs();\n\n\t\t\tthis.$log( { action: 'search_clear' } );\n\t\t},\n\n\t\t/**\n\t\t * @param {PopStateEvent} e\n\t\t * @param {Object} [e.state]\n\t\t */\n\t\tonPopState: function ( e ) {\n\t\t\t// If the newly-active history entry includes a state object, use it\n\t\t\t// to reset the URL query params and the UI state\n\t\t\tif ( e.state ) {\n\t\t\t\tthis.setTerm( e.state.q || '' );\n\t\t\t\tthis.currentTab = e.state.type;\n\n\t\t\t\t// if the newly-active history entry includes a \"null\" query\n\t\t\t\t// (the result of clicking the clear button, for example),\n\t\t\t\t// ensure that the results are reset\n\t\t\t\tif ( this.term === '' ) {\n\t\t\t\t\tthis.resetResults();\n\t\t\t\t\tthis.clearRelatedConcepts();\n\t\t\t\t\tthis.clearLookupResults();\n\t\t\t\t}\n\n\t\t\t\t// Retreive any previously-active filters from the state object\n\t\t\t\t// and manipulate the current state of both the application and\n\t\t\t\t// the URL object to match the previously-stored values\n\t\t\t\tthis.resetFilters();\n\t\t\t\tthis.clearFilterQueryParams();\n\t\t\t\turl.query.q = this.term;\n\t\t\t\turl.query.type = this.currentTab;\n\n\t\t\t\tObject.keys( e.state ).forEach( function ( key ) {\n\t\t\t\t\tif ( key in searchOptions[ this.currentTab ] ) {\n\t\t\t\t\t\turl.query[ key ] = e.state[ key ];\n\n\t\t\t\t\t\tthis.addFilterValue( {\n\t\t\t\t\t\t\tmediaType: this.currentTab,\n\t\t\t\t\t\t\tfilterType: key,\n\t\t\t\t\t\t\tvalue: e.state[ key ]\n\t\t\t\t\t\t} );\n\t\t\t\t\t}\n\t\t\t\t}.bind( this ) );\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * @param {string} tab bitmap, audio, etc.\n\t\t */\n\t\tgetMoreResultsForTabIfAvailable: function ( tab ) {\n\t\t\t// Don't make API requests if the search term is empty\n\t\t\tif ( this.term === '' ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( this.checkForMore[ tab ] && !this.pending[ tab ] ) {\n\t\t\t\t// Decrement the autoload count of the appropriate tab\n\t\t\t\tthis.autoloadCounter[ tab ]--;\n\n\t\t\t\t// If more results are available, and if another request is not\n\t\t\t\t// already pending, then launch a search request\n\t\t\t\tthis.search( {\n\t\t\t\t\tterm: this.term,\n\t\t\t\t\ttype: this.currentTab\n\t\t\t\t} ).then( function () {\n\t\t\t\t\t/* eslint-disable camelcase */\n\t\t\t\t\tthis.$log( {\n\t\t\t\t\t\taction: 'search_load_more',\n\t\t\t\t\t\tsearch_query: this.term,\n\t\t\t\t\t\tsearch_media_type: this.currentTab,\n\t\t\t\t\t\tsearch_result_count: this.results[ this.currentTab ].length\n\t\t\t\t\t} );\n\t\t\t\t\t/* eslint-enable camelcase */\n\t\t\t\t}.bind( this ) );\n\n\t\t\t} else if ( this.checkForMore[ tab ] && this.pending[ tab ] ) {\n\t\t\t\t// If more results are available but another request is\n\t\t\t\t// currently in-flight, attempt to make the request again\n\t\t\t\t// after some time has passed\n\t\t\t\twindow.setTimeout(\n\t\t\t\t\tthis.getMoreResultsForTabIfAvailable.bind( this, tab ),\n\t\t\t\t\t2000\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\n\t\tresetCountAndLoadMore: function ( tab ) {\n\t\t\t// Don't make API requests if the search term is empty\n\t\t\tif ( this.term === '' ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Reset the autoload count for the given tab\n\t\t\tthis.autoloadCounter[ tab ] = AUTOLOAD_COUNT;\n\n\t\t\t// Launch a search request\n\t\t\tthis.search( {\n\t\t\t\tterm: this.term,\n\t\t\t\ttype: this.currentTab\n\t\t\t} ).then( function () {\n\t\t\t\t/* eslint-disable camelcase */\n\t\t\t\tthis.$log( {\n\t\t\t\t\taction: 'search_load_more',\n\t\t\t\t\tsearch_query: this.term,\n\t\t\t\t\tsearch_media_type: this.currentTab,\n\t\t\t\t\tsearch_result_count: this.results[ this.currentTab ].length\n\t\t\t\t} );\n\t\t\t\t/* eslint-enable camelcase */\n\t\t\t}.bind( this ) );\n\t\t},\n\n\t\t/**\n\t\t * Dispatch Vuex actions to clear existing results and fetch new ones.\n\t\t * Also resets the autoload counter for all tabs for semi-infinite\n\t\t * scroll behavior.\n\t\t *\n\t\t * @param {string} mediaType If provided, only reset results for this type\n\t\t */\n\t\tperformNewSearch: function ( mediaType ) {\n\t\t\tthis.resetResults( mediaType );\n\t\t\tthis.clearRelatedConcepts();\n\t\t\tthis.clearDidYouMean();\n\t\t\tthis.autoloadCounter = this.setInitialAutoloadCountForTabs();\n\n\t\t\t// Abort in-flight lookup promises to ensure the results provided\n\t\t\t// are for the most recent search input.\n\t\t\tif ( this.lookupPromises ) {\n\t\t\t\tthis.lookupPromises.abort();\n\t\t\t}\n\n\t\t\t// Don't make API requests if the search term is empty\n\t\t\tif ( this.term === '' ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.search( {\n\t\t\t\tterm: this.term,\n\t\t\t\ttype: this.currentTab\n\t\t\t} ).then( function () {\n\t\t\t\t/* eslint-disable camelcase */\n\t\t\t\tthis.$log( {\n\t\t\t\t\taction: 'search_new',\n\t\t\t\t\tsearch_query: this.term,\n\t\t\t\t\tsearch_media_type: this.currentTab,\n\t\t\t\t\tsearch_result_count: this.results[ this.currentTab ].length\n\t\t\t\t} );\n\t\t\t\t/* eslint-enable camelcase */\n\t\t\t}.bind( this ) );\n\t\t},\n\n\t\t/**\n\t\t * @return {Object} counter object broken down by tab name\n\t\t */\n\t\tsetInitialAutoloadCountForTabs: function () {\n\t\t\tvar count = {};\n\n\t\t\tthis.tabs.forEach( function ( tabName ) {\n\t\t\t\tcount[ tabName ] = AUTOLOAD_COUNT;\n\t\t\t} );\n\n\t\t\treturn count;\n\t\t},\n\n\t\t/**\n\t\t * Delete all filter query params from the mw.Uri object but leave any\n\t\t * other filters such as debug mode, feature flags, etc. intact.\n\t\t */\n\t\tclearFilterQueryParams: function () {\n\t\t\tObject.keys( searchOptions ).forEach( function ( type ) {\n\t\t\t\tObject.keys( searchOptions[ type ] ).forEach( function ( filter ) {\n\t\t\t\t\tdelete url.query[ filter ];\n\t\t\t\t} );\n\t\t\t} );\n\n\t\t\t// Delete any sort params that may have been added from a prior tab\n\t\t\tdelete url.query.sort;\n\t\t}\n\t} ),\n\n\twatch: {\n\t\t/**\n\t\t * When the currentTab changes, fetch more results for the new tab if\n\t\t * available\n\t\t *\n\t\t * @param {string} newTab bitmap, audio, etc.\n\t\t * @param {string} oldTab bitmap, audio, etc.\n\t\t */\n\t\tcurrentTab: function ( newTab, oldTab ) {\n\t\t\tif ( newTab && newTab !== oldTab ) {\n\t\t\t\tthis.getMoreResultsForTabIfAvailable( newTab );\n\n\t\t\t\tif ( this.enableConceptChips && newTab === 'bitmap' && this.relatedConcepts.length < 1 ) {\n\t\t\t\t\tthis.getRelatedConcepts( this.term );\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * If the new term does not match what previously existed here, perform\n\t\t * a new search.\n\t\t *\n\t\t * @param {string} newTerm\n\t\t * @param {string} oldTerm\n\t\t */\n\t\tterm: function ( newTerm, oldTerm ) {\n\t\t\tif ( newTerm && newTerm !== oldTerm ) {\n\t\t\t\tthis.performNewSearch();\n\n\t\t\t\tif ( this.enableConceptChips && this.currentTab === 'bitmap' ) {\n\t\t\t\t\tthis.getRelatedConcepts( newTerm );\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tallActiveFilters: function ( newVal, oldVal ) {\n\t\t\tif ( newVal && newVal !== oldVal ) {\n\t\t\t\tthis.performNewSearch( this.currentTab );\n\t\t\t}\n\t\t}\n\t},\n\n\tcreated: function () {\n\t\t// If user arrives on the page without URL params to specify initial search\n\t\t// type / active tab, default to bitmap. This is done in created hook\n\t\t// because some computed properties assume that a currentTab will always be\n\t\t// specified; the created hook runs before computed properties are evaluated.\n\t\tif ( this.currentTab === '' ) {\n\t\t\tthis.currentTab = 'bitmap';\n\t\t\turl.query.type = 'bitmap';\n\t\t}\n\n\t\t// Record whatever the initial query params are that the user arrived on\n\t\t// the page with as \"state\" in the history stack using history.replaceState;\n\t\t// this will enable us to access it later if the user starts navigating\n\t\t// through history states\n\t\twindow.history.replaceState( url.query, null, '?' + url.getQueryString() );\n\n\t\t// Set up a listener for popState events in case the user navigates\n\t\t// through their history stack. Previous search queries should be\n\t\t// re-created when this happens, and URL params and UI state should\n\t\t// remain in sync\n\n\t\t// First, create a bound handler function and reference it for later removal\n\t\tthis.boundOnPopState = this.onPopState.bind( this );\n\n\t\t// Set up the event listener\n\t\twindow.addEventListener( 'popstate', this.boundOnPopState );\n\n\t\t// Set the initial autoload count for all tabs for semi-infinite scroll\n\t\t// behavior\n\t\tthis.autoloadCounter = this.setInitialAutoloadCountForTabs();\n\n\t\t// If a search term exists on page load, fetch related concepts for\n\t\t// concept chips.\n\t\tif ( this.enableConceptChips && this.term && this.currentTab === 'bitmap' ) {\n\t\t\tthis.getRelatedConcepts( this.term );\n\t\t}\n\n\t\t// If a search term was already present when the user arrives on the page,\n\t\t// log the results of the initial server-rendered search query regardless\n\t\t// of whether any results were found\n\t\tif ( this.term && this.term !== '' ) {\n\t\t\t/* eslint-disable camelcase */\n\t\t\tthis.$log( {\n\t\t\t\taction: 'search_new',\n\t\t\t\tsearch_query: this.term,\n\t\t\t\tsearch_media_type: this.currentTab,\n\t\t\t\tsearch_result_count: this.results[ this.currentTab ].length\n\t\t\t} );\n\t\t\t/* eslint-enable camelcase */\n\t\t}\n\t},\n\n\tbeforeDestroy: function () {\n\t\twindow.removeEventListener( 'popstate', this.boundOnPopState );\n\t}\n};\n</script>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/ConceptChips.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/DidYouMean.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/EmptyState.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/EndOfResults.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/NoResults.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/QuickView.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/SearchFilters.vue","messages":[{"ruleId":"compat/compat","severity":2,"message":"IntersectionObserverEntry is not supported in Safari 5.1, IE 11","line":110,"column":28,"nodeType":"MemberExpression","endLine":110,"endColumn":60}],"errorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div class=\"wbmi-media-search-filters-wrapper\" :class=\"rootClasses\">\n\t\t<div class=\"wbmi-media-search-filters\">\n\t\t\t<template v-for=\"( filter, index ) in searchFilters\">\n\t\t\t\t<wbmi-select\n\t\t\t\t\t:ref=\"filter.type\"\n\t\t\t\t\t:key=\"'filter-' + index\"\n\t\t\t\t\t:class=\"getFilterClasses( filter.type )\"\n\t\t\t\t\t:name=\"filter.type\"\n\t\t\t\t\t:items=\"filter.items\"\n\t\t\t\t\t:initial-selected-item-index=\"0\"\n\t\t\t\t\t:prefix=\"getFilterPrefix( filter.type )\"\n\t\t\t\t\t@select=\"onSelect( $event, filter.type )\"\n\t\t\t\t>\n\t\t\t\t</wbmi-select>\n\t\t\t\t<wbmi-observer\n\t\t\t\t\tv-if=\"index === searchFilters.length - 1 && supportsObserver\"\n\t\t\t\t\t:key=\"'filter-observer-' + index\"\n\t\t\t\t\t@intersect=\"removeGradientClass\"\n\t\t\t\t\t@hide=\"addGradientClass\"\n\t\t\t\t></wbmi-observer>\n\t\t\t</template>\n\t\t\t<span v-if=\"showResultsCount\" class=\"wbmi-media-search-results-count\">\n\t\t\t\t{{ resultsCount }}\n\t\t\t</span>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\n/**\n * @file SearchFilters.vue\n *\n * Container for the search filters for a tab. Displays the filters and handles\n * change in filter value. When a filter value changes, the Vuex state is\n * updated with the new filter value, and a new-search event is emitted so the\n * parent App component can dispatch the search action.\n */\nvar mapState = require( 'vuex' ).mapState,\n\tmapMutations = require( 'vuex' ).mapMutations,\n\tWbmiSelect = require( './base/Select.vue' ),\n\tWbmiObserver = require( './base/Observer.vue' ),\n\tSearchFilter = require( '../models/SearchFilter.js' ),\n\tsearchOptions = require( './../data/searchOptions.json' );\n\n// @vue/component\nmodule.exports = {\n\tname: 'SearchFilters',\n\n\tcomponents: {\n\t\t'wbmi-select': WbmiSelect,\n\t\t'wbmi-observer': WbmiObserver\n\t},\n\n\tprops: {\n\t\tmediaType: {\n\t\t\ttype: String,\n\t\t\trequired: true\n\t\t}\n\t},\n\n\tdata: function () {\n\t\treturn {\n\t\t\thasGradient: false\n\t\t};\n\t},\n\n\tcomputed: $.extend( {}, mapState( [\n\t\t'totalHits',\n\t\t'filterValues'\n\t] ), {\n\t\t/**\n\t\t * @return {Object}\n\t\t */\n\t\trootClasses: function () {\n\t\t\treturn {\n\t\t\t\t'wbmi-media-search-filters-wrapper--gradient': this.hasGradient\n\t\t\t};\n\t\t},\n\n\t\t/**\n\t\t * @return {Array} SearchFilter objects for this media type.\n\t\t */\n\t\tsearchFilters: function () {\n\t\t\tvar optionsForType = searchOptions[ this.mediaType ],\n\t\t\t\tfilters = [];\n\n\t\t\tObject.keys( optionsForType ).forEach( function ( option ) {\n\t\t\t\tvar filter = new SearchFilter( option, optionsForType[ option ] );\n\t\t\t\tfilters.push( filter );\n\t\t\t} );\n\n\t\t\treturn filters;\n\t\t},\n\n\t\t/**\n\t\t * Key names (not values) of all active filters for the given tab;\n\t\t * Having a shorthand computed property for this makes it easier to\n\t\t * watch for changes.\n\t\t *\n\t\t * @return {Array} Empty array or [ \"imageSize\", \"mimeType\" ], etc\n\t\t */\n\t\tcurrentActiveFilters: function () {\n\t\t\treturn Object.keys( this.filterValues[ this.mediaType ] );\n\t\t},\n\n\t\tsupportsObserver: function () {\n\t\t\treturn 'IntersectionObserver' in window &&\n\t\t\t\t'IntersectionObserverEntry' in window &&\n\t\t\t\t'intersectionRatio' in window.IntersectionObserverEntry.prototype;\n\t\t},\n\n\t\t/**\n\t\t * Number of results should only display if results exist.\n\t\t *\n\t\t * @return {boolean} Whether to display the results count\n\t\t */\n\t\tshowResultsCount: function () {\n\t\t\treturn this.totalHits[ this.mediaType ] > 0;\n\t\t},\n\n\t\t/**\n\t\t * String representing the number of search results.\n\t\t *\n\t\t * @return {Object} Message object\n\t\t */\n\t\tresultsCount: function () {\n\t\t\treturn this.$i18n(\n\t\t\t\t'wikibasemediainfo-special-mediasearch-results-count',\n\t\t\t\tthis.totalHits[ this.mediaType ].toLocaleString( mw.config.get( 'wgUserLanguage' ) )\n\t\t\t);\n\t\t}\n\t} ),\n\n\tmethods: $.extend( {}, mapMutations( [\n\t\t'addFilterValue',\n\t\t'removeFilterValue'\n\t] ), {\n\t\t/**\n\t\t * Handle filter change.\n\t\t *\n\t\t * @param {string} value The new filter value\n\t\t * @param {string} filterType\n\t\t * @fires filter-change\n\t\t */\n\t\tonSelect: function ( value, filterType ) {\n\t\t\tvar oldValue = this.filterValues[ this.mediaType ][ filterType ] || '';\n\n\t\t\tif ( value ) {\n\t\t\t\tthis.addFilterValue( {\n\t\t\t\t\tmediaType: this.mediaType,\n\t\t\t\t\tfilterType: filterType,\n\t\t\t\t\tvalue: value\n\t\t\t\t} );\n\t\t\t\t/* eslint-disable camelcase */\n\t\t\t\tthis.$log( {\n\t\t\t\t\taction: 'filter_change',\n\t\t\t\t\tsearch_media_type: this.mediaType,\n\t\t\t\t\tsearch_filter_type: filterType,\n\t\t\t\t\tsearch_filter_value: value,\n\t\t\t\t\tprior_search_filter_type: filterType,\n\t\t\t\t\tprior_search_filter_value: oldValue\n\t\t\t\t} );\n\t\t\t\t/* eslint-enable camelcase */\n\t\t\t} else {\n\t\t\t\tthis.removeFilterValue( {\n\t\t\t\t\tmediaType: this.mediaType,\n\t\t\t\t\tfilterType: filterType\n\t\t\t\t} );\n\n\t\t\t\t/* eslint-disable camelcase */\n\t\t\t\tthis.$log( {\n\t\t\t\t\taction: 'filter_change',\n\t\t\t\t\tsearch_media_type: this.mediaType,\n\t\t\t\t\tsearch_filter_type: filterType,\n\t\t\t\t\tsearch_filter_value: '',\n\t\t\t\t\tprior_search_filter_type: filterType,\n\t\t\t\t\tprior_search_filter_value: oldValue\n\t\t\t\t} );\n\t\t\t\t/* eslint-enable camelcase */\n\t\t\t}\n\n\t\t\t// Tell the App component to do a new search and update the URL\n\t\t\t// params\n\t\t\tthis.$emit( 'filter-change', {\n\t\t\t\tmediaType: this.mediaType,\n\t\t\t\tfilterType: filterType,\n\t\t\t\tvalue: value\n\t\t\t} );\n\t\t},\n\n\t\t/**\n\t\t * We need a class for select lists where a non-default item is selected.\n\t\t *\n\t\t * @param {string} filterType\n\t\t * @return {Object}\n\t\t */\n\t\tgetFilterClasses: function ( filterType ) {\n\t\t\treturn {\n\t\t\t\t'wbmi-search-filter--selected': this.currentActiveFilters.indexOf( filterType ) !== -1\n\t\t\t};\n\t\t},\n\n\t\t/**\n\t\t * Add select list prefixes per filter type.\n\t\t *\n\t\t * @param {string} filterType\n\t\t * @return {string}\n\t\t */\n\t\tgetFilterPrefix: function ( filterType ) {\n\t\t\tif ( filterType === 'sort' ) {\n\t\t\t\treturn this.$i18n( 'wikibasemediainfo-special-mediasearch-filter-sort-label' );\n\t\t\t}\n\n\t\t\treturn '';\n\t\t},\n\n\t\t/**\n\t\t * When final filter is out of view, add class that will add a gradient\n\t\t * to indicate to the user that they can horizontally scroll.\n\t\t */\n\t\taddGradientClass: function () {\n\t\t\tthis.hasGradient = true;\n\t\t},\n\n\t\t/**\n\t\t * When final filter is in view, don't show the gradient.\n\t\t */\n\t\tremoveGradientClass: function () {\n\t\t\tthis.hasGradient = false;\n\t\t},\n\n\t\tresetAllFilters: function () {\n\t\t\tthis.searchFilters.forEach( function ( filter ) {\n\t\t\t\tthis.$refs[ filter.type ][ 0 ].reset();\n\t\t\t}.bind( this ) );\n\t\t},\n\n\t\t/**\n\t\t * Set each filter component's state to match the appropriate\n\t\t * value in Vuex\n\t\t */\n\t\tsynchronizeFilters: function () {\n\t\t\tthis.searchFilters.forEach( function ( filter ) {\n\t\t\t\tvar currentValue = this.filterValues[ this.mediaType ][ filter.type ];\n\n\t\t\t\tif ( currentValue ) {\n\t\t\t\t\tthis.$refs[ filter.type ][ 0 ].select( currentValue );\n\t\t\t\t} else {\n\t\t\t\t\tthis.$refs[ filter.type ][ 0 ].reset();\n\t\t\t\t}\n\t\t\t}.bind( this ) );\n\t\t}\n\t} ),\n\n\twatch: {\n\t\t/**\n\t\t * Programmatically set or reset filters if Vuex state changes for\n\t\t * reasons other than the user setting filters manually (clicking the\n\t\t * clear button, URL filter params, popstate, etc)\n\t\t */\n\t\tcurrentActiveFilters: function () {\n\t\t\tthis.synchronizeFilters();\n\t\t}\n\t},\n\n\t/**\n\t * If filters have already been set at the time of page initialization\n\t * via URL params, update the relevant Select child component with\n\t * the appropriate value\n\t */\n\tmounted: function () {\n\t\tthis.synchronizeFilters();\n\t}\n};\n</script>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/SearchResults.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/Spinner.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/AutocompleteSearchInput.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/Button.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/CopyTextLayout.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/Dialog.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/Icon.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/Image.vue","messages":[{"ruleId":"compat/compat","severity":2,"message":"IntersectionObserverEntry is not supported in Safari 5.1, IE 11","line":52,"column":28,"nodeType":"MemberExpression","endLine":52,"endColumn":60}],"errorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<img\n\t\t:src=\"supportsObserver ? false : source\"\n\t\t:data-src=\"source\"\n\t\t:alt=\"alt\"\n\t\tclass=\"wbmi-image\"\n\t\tloading=\"lazy\"\n\t\t:style=\"imageStyle\"\n\t>\n</template>\n\n<script>\n\n// @vue/component\nmodule.exports = {\n\tprops: {\n\t\tsource: {\n\t\t\ttype: String,\n\t\t\trequired: true\n\t\t},\n\n\t\talt: {\n\t\t\ttype: String,\n\t\t\tdefault: ''\n\t\t},\n\n\t\toriginalWidth: {\n\t\t\ttype: Number,\n\t\t\tdefault: 0\n\t\t},\n\n\t\toriginalHeight: {\n\t\t\ttype: Number,\n\t\t\tdefault: 0\n\t\t}\n\t},\n\n\tdata: function () {\n\t\treturn {\n\t\t\tisIntersecting: false,\n\t\t\tdebounceTimeoutId: null\n\t\t};\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * @return {boolean}\n\t\t */\n\t\tsupportsObserver: function () {\n\t\t\treturn 'IntersectionObserver' in window &&\n\t\t\t\t'IntersectionObserverEntry' in window &&\n\t\t\t\t'intersectionRatio' in window.IntersectionObserverEntry.prototype;\n\t\t},\n\n\t\t/**\n\t\t * If we have image dimensions, add a style attribute to constrain the\n\t\t * image to its original size to avoid stretching a small image.\n\t\t *\n\t\t * @return {Object|boolean}\n\t\t */\n\t\timageStyle: function () {\n\t\t\tif ( this.originalWidth > 0 && this.originalHeight > 0 ) {\n\t\t\t\treturn {\n\t\t\t\t\t// There are height and max-width rules with the important\n\t\t\t\t\t// keyword for .content a > img in Minerva Neue, and they\n\t\t\t\t\t// have to be overridden.\n\t\t\t\t\t// I don't see any other way around this...\n\t\t\t\t\theight: '100% !important',\n\t\t\t\t\tmaxWidth: this.originalWidth + 'px !important',\n\t\t\t\t\tmaxHeight: this.originalHeight + 'px'\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\t},\n\n\t/**\n\t * Use an intersection observer to determine when the image has entered the\n\t * viewport, then add the src attribute.\n\t */\n\tmounted: function () {\n\t\tfunction loadImageIfIntersecting() {\n\t\t\tif ( this.isIntersecting ) {\n\t\t\t\t// set the \"src\" attribute so the image loads\n\t\t\t\tthis.$el.src = this.$el.dataset.src;\n\n\t\t\t\t// remove the element from the observer's watch list so it\n\t\t\t\t// doesn't keep getting called\n\t\t\t\tthis.observer.unobserve( this.$el );\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Callback function which is given to the Observer object; this is\n\t\t * what gets executed when the element enters the viewport\n\t\t *\n\t\t * @param {Array} entries array of elements watched by the observer\n\t\t */\n\t\tfunction intersectionCallback( entries ) {\n\t\t\tvar entry = entries[ 0 ];\n\n\t\t\tthis.isIntersecting = entry && entry.isIntersecting;\n\n\t\t\t// debounce to avoid loading images that are rapidly scrolled\n\t\t\t// out of screen anyway\n\t\t\tclearTimeout( this.debounceTimeoutId );\n\t\t\tthis.debounceTimeoutId = setTimeout(\n\t\t\t\tloadImageIfIntersecting.bind( this ),\n\t\t\t\t250\n\t\t\t);\n\t\t}\n\n\t\tif ( this.supportsObserver ) {\n\t\t\tthis.observer = new IntersectionObserver(\n\t\t\t\tintersectionCallback.bind( this ), {\n\t\t\t\t\t// Set the detection area to extend past the bottom of the\n\t\t\t\t\t// viewport by 50% (a figure that comes from MobileFrontEnd) so\n\t\t\t\t\t// images will load before they enter the viewport.\n\t\t\t\t\trootMargin: '0px 0px 50% 0px',\n\t\t\t\t\tthreshold: 0\n\t\t\t\t} );\n\n\t\t\tthis.observer.observe( this.$el );\n\t\t}\n\t},\n\n\t/**\n\t * Disconnect the observer when the component is destroyed\n\t */\n\tdestroyed: function () {\n\t\tif ( this.supportsObserver ) {\n\t\t\tthis.observer.disconnect();\n\t\t}\n\t}\n};\n</script>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/LookupResults.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/Message.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/Observer.vue","messages":[{"ruleId":"compat/compat","severity":2,"message":"IntersectionObserver is not supported in Safari 5.1, IE 11, android 4.1","line":52,"column":19,"nodeType":"NewExpression","endLine":55,"endColumn":4}],"errorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div class=\"wbmi-observer\"></div>\n</template>\n\n<script>\n/**\n * Observer.vue\n *\n * This component is a simple wrapper for an Intersection Observer object\n * (https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API),\n * which provides a simple way to determine when an element intersects with the\n * viewport (or a specific subsection of the viewport).\n *\n * By itself this component should have no visual or behavioral impact on the\n * UI. It simply emits an \"intersect\" event which can be handled by the parent\n * as needed. Add this component to the end of a list for an \"infinite scroll\"\n * effect.\n */\nmodule.exports = {\n\tname: 'WbmiObserver',\n\n\tprops: {\n\t\toptions: Object\n\t},\n\n\tdata: function () {\n\t\treturn {\n\t\t\tobserver: null\n\t\t};\n\t},\n\n\t/**\n\t * Create an intersection observer when the Observer component mounts\n\t */\n\tmounted: function () {\n\t\tvar options = this.options || {};\n\n\t\tfunction intersectionCallback( entries ) {\n\t\t\t// An array of entries will be passed to the callback,\n\t\t\t// but we only care about the first element\n\t\t\tvar entry = entries[ 0 ];\n\n\t\t\tif ( entry && entry.isIntersecting ) {\n\t\t\t\tthis.$emit( 'intersect' );\n\t\t\t}\n\n\t\t\tif ( entry && !entry.isIntersecting ) {\n\t\t\t\tthis.$emit( 'hide' );\n\t\t\t}\n\t\t}\n\n\t\tthis.observer = new IntersectionObserver(\n\t\t\tintersectionCallback.bind( this ), // what to do when intersection occurs\n\t\t\toptions // additional options can be provided as props to this component\n\t\t);\n\n\t\tthis.observer.observe( this.$el );\n\t},\n\n\t/**\n\t * Disconnect the observer when the component is destroyed\n\t */\n\tdestroyed: function () {\n\t\tthis.observer.disconnect();\n\t}\n};\n</script>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/Player.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/Select.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/SelectMenu.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/Tab.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/base/Tabs.vue","messages":[{"ruleId":"compat/compat","severity":2,"message":"IntersectionObserverEntry is not supported in Safari 5.1, IE 11","line":95,"column":28,"nodeType":"MemberExpression","endLine":95,"endColumn":60}],"errorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div class=\"wbmi-tabs\">\n\t\t<div class=\"wbmi-tabs__header\" :class=\"headerClasses\">\n\t\t\t<div\n\t\t\t\tclass=\"wbmi-tabs__tabs-list\"\n\t\t\t\trole=\"tablist\"\n\t\t\t\ttabindex=\"0\"\n\t\t\t\t:aria-activedescendant=\"currentTabId\"\n\t\t\t\t@keydown.left=\"moveBack\"\n\t\t\t\t@keydown.up.prevent=\"moveBack\"\n\t\t\t\t@keydown.right=\"moveForward\"\n\t\t\t\t@keydown.down.prevent=\"moveForward\"\n\t\t\t>\n\t\t\t\t<div v-for=\"( tab, index ) in tabs\"\n\t\t\t\t\t:id=\"tab.id + '-label'\"\n\t\t\t\t\t:key=\"tab.title\"\n\t\t\t\t\t:class=\"determineTabLabelClasses( tab )\"\n\t\t\t\t\t:aria-selected=\"tab.name === currentTabName\"\n\t\t\t\t\t:aria-controls=\"tab.id\"\n\t\t\t\t\tclass=\"wbmi-tabs__tabs-list__item\"\n\t\t\t\t\trole=\"tab\"\n\t\t\t\t\ttabindex=\"-1\"\n\t\t\t\t\t@click=\"selectTab( tab.name )\"\n\t\t\t\t\t@keyup.enter=\"selectTab( tab.name )\"\n\t\t\t\t>\n\t\t\t\t\t{{ tab.title }}\n\t\t\t\t\t<observer\n\t\t\t\t\t\tv-if=\"isLastTab( index ) && supportsObserver\"\n\t\t\t\t\t\t@intersect=\"removeGradientClass\"\n\t\t\t\t\t\t@hide=\"addGradientClass\"\n\t\t\t\t\t></observer>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<div class=\"wbmi-tabs__content\">\n\t\t\t<slot></slot>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nvar Vue = require( 'vue' ), // Vue is imported here for type definition\n\tObserver = require( './Observer.vue' );\n\n/**\n * A group of tabs with a tab menu.\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 * This component has two slots: the main slot, which is meant to contain Tab\n * components, and the filters slot, which can contain Select components that\n * will be displayed inline with the tabs list in the heading.\n */\n// @vue/component\nmodule.exports = {\n\tname: 'WbmiTabs',\n\n\tcomponents: {\n\t\tobserver: Observer\n\t},\n\n\tprops: {\n\t\tactive: {\n\t\t\ttype: String,\n\t\t\tdefault: null\n\t\t}\n\t},\n\n\tdata: function () {\n\t\treturn {\n\t\t\ttabs: {},\n\t\t\tcurrentTabName: null,\n\t\t\thasGradient: false\n\t\t};\n\t},\n\n\tcomputed: {\n\t\theaderClasses: function () {\n\t\t\treturn {\n\t\t\t\t'wbmi-tabs__header--gradient': this.hasGradient\n\t\t\t};\n\t\t},\n\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\n\t\tsupportsObserver: function () {\n\t\t\treturn 'IntersectionObserver' in window &&\n\t\t\t\t'IntersectionObserverEntry' in window &&\n\t\t\t\t'intersectionRatio' in window.IntersectionObserverEntry.prototype;\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 {Vue.component} tab\n\t\t * @return {Object}\n\t\t */\n\t\tdetermineTabLabelClasses: function ( tab ) {\n\t\t\treturn {\n\t\t\t\t'wbmi-tabs__tabs-list__item--current': tab.name === this.currentTabName,\n\t\t\t\t'wbmi-tabs__tabs-list__item--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\tvar tabs = this.$slots.default;\n\t\t\tthis.tabs = {};\n\n\t\t\ttabs.forEach( function ( tab ) {\n\t\t\t\tthis.tabs[ tab.componentInstance.name ] = tab.componentInstance;\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\n\t\t/**\n\t\t * @param {string} mediaType bitmap, audio, video, etc.\n\t\t * @return {boolean}\n\t\t */\n\t\tisLastTab: function ( mediaType ) {\n\t\t\tvar tabKeys = Object.keys( this.tabs );\n\t\t\treturn mediaType === tabKeys[ tabKeys.length - 1 ];\n\t\t},\n\n\t\t/**\n\t\t * When final tab is out of view, add class that will add a gradient to\n\t\t * indicate to the user that they can horizontally scroll.\n\t\t */\n\t\taddGradientClass: function () {\n\t\t\tthis.hasGradient = true;\n\t\t},\n\n\t\t/**\n\t\t * When final tab is in view, don't show the gradient.\n\t\t */\n\t\tremoveGradientClass: function () {\n\t\t\tthis.hasGradient = false;\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","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/results/AudioResult.vue","messages":[{"ruleId":"max-len","severity":1,"message":"This line has a length of 119. Maximum allowed is 100.","line":16,"column":1,"nodeType":"Program","messageId":"max","endLine":16,"endColumn":108}],"errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div class=\"wbmi-audio-result\">\n\t\t<h3 class=\"wbmi-audio-result__title\">\n\t\t\t<a ref=\"link\"\n\t\t\t\t:href=\"canonicalurl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\t:title=\"title\"\n\t\t\t\t@click=\"showDetails\">\n\t\t\t\t{{ displayName }}\n\t\t\t</a>\n\t\t</h3>\n\n\t\t<h4 class=\"wbmi-audio-result__meta\">\n\t\t\t<span class=\"wbmi-audio-result__duration\">\n\t\t\t\t<wbmi-icon :icon=\"icon\"></wbmi-icon>\n\t\t\t\t<span v-if=\"formattedDuration\" class=\"wbmi-audio-result__duration__text\">{{ formattedDuration }}</span>\n\t\t\t</span>\n\t\t\t<span v-if=\"mime\" class=\"wbmi-audio-result__mime\">{{ mime }}</span>\n\t\t</h4>\n\n\t\t<p v-if=\"label\">\n\t\t\t{{ label }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\n/**\n * @file AudioResult.vue\n *\n * Audio-specific search result layout. Implements the general searchResult\n * mixin as well as the \"time-based\" result mixin.\n */\nvar searchResult = require( '../../mixins/searchResult.js' ),\n\tsearchResultTimeBased = require( '../../mixins/searchResultTimeBased.js' ),\n\tWbmiIcon = require( '../base/Icon.vue' ),\n\ticons = require( '../../../../lib/icons.js' );\n\n// @vue/component\nmodule.exports = {\n\tname: 'AudioResult',\n\n\tcomponents: {\n\t\t'wbmi-icon': WbmiIcon\n\t},\n\n\tmixins: [\n\t\tsearchResult,\n\t\tsearchResultTimeBased\n\t],\n\n\tinheritAttrs: false,\n\n\tdata: function () {\n\t\treturn {\n\t\t\ticon: icons.wbmiIconVolumeUp\n\t\t};\n\t}\n};\n</script>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/results/ImageResult.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/results/OtherResult.vue","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/results/PageResult.vue","messages":[{"ruleId":"vue/no-v-html","severity":1,"message":"'v-html' directive can lead to XSS attack.","line":17,"column":23,"nodeType":"VAttribute","endLine":17,"endColumn":39}],"errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<div class=\"wbmi-page-result\">\n\t\t<div class=\"wbmi-page-result__title\">\n\t\t\t<span class=\"wbmi-page-result__namespace\">\n\t\t\t\t{{ namespacePrefix }}\n\t\t\t</span>\n\t\t\t<h3>\n\t\t\t\t<a :href=\"canonicalurl\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t:title=\"title\"\n\t\t\t\t\t@click=\"$emit('click')\">\n\t\t\t\t\t{{ displayName }}\n\t\t\t\t</a>\n\t\t\t</h3>\n\t\t</div>\n\n\t\t<div v-if=\"snippet\" v-html=\"snippet\"></div>\n\n\t\t<p v-if=\"hasCategoryText\"\n\t\t\tv-i18n-html:wikibasemediainfo-special-mediasearch-category-info=\"[\n\t\t\t\tformatNumber( categoryinfo.size ),\n\t\t\t\tformatNumber( categoryinfo.subcats ),\n\t\t\t\tformatNumber( categoryinfo.files )\n\t\t\t]\">\n\t\t</p>\n\n\t\t<template v-else>\n\t\t\t<p v-if=\"size\">\n\t\t\t\t{{ formatSize( size ) }}\n\t\t\t</p>\n\n\t\t\t<p v-if=\"wordcount\"\n\t\t\t\tv-i18n-html:wikibasemediainfo-special-mediasearch-wordcount=\"[\n\t\t\t\t\tformatNumber( wordcount )\n\t\t\t\t]\">\n\t\t\t</p>\n\t\t</template>\n\n\t\t<p v-if=\"lastEdited\">\n\t\t\t- {{ lastEdited }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\n/**\n * @file PageResult.vue\n *\n * Represents page and category results.\n */\nvar searchResult = require( '../../mixins/searchResult.js' ),\n\tuserLanguage = mw.config.get( 'wgUserLanguage' );\n\n// @vue/component\nmodule.exports = {\n\tname: 'PageResult',\n\n\tmixins: [ searchResult ],\n\n\tinheritAttrs: false,\n\n\tprops: {\n\t\tcategoryinfo: {\n\t\t\ttype: Object,\n\t\t\tdefault: function () {\n\t\t\t\treturn {};\n\t\t\t}\n\t\t},\n\n\t\tsize: {\n\t\t\ttype: Number,\n\t\t\tdefault: null\n\t\t},\n\n\t\twordcount: {\n\t\t\ttype: Number,\n\t\t\tdefault: null\n\t\t},\n\n\t\ttimestamp: {\n\t\t\ttype: String,\n\t\t\tdefault: null\n\t\t},\n\n\t\tsnippet: {\n\t\t\ttype: String,\n\t\t\tdefault: null\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * @return {boolean}\n\t\t */\n\t\thasCategoryText: function () {\n\t\t\treturn Object.keys( this.categoryinfo ).length > 0;\n\t\t},\n\n\t\t/**\n\t\t * @return {string}\n\t\t */\n\t\tnamespacePrefix: function () {\n\t\t\tvar title = new mw.Title( this.title );\n\n\t\t\t// If this is the default namespace (gallery), getNamespacePrefix()\n\t\t\t// won't return anything, so we need to use this system message\n\t\t\t// instead, which is configured at MediaWiki:Blanknamespace.\n\t\t\t// Otherwise, return the namespace prefix with the trailing\n\t\t\t// colon stripped off.\n\t\t\treturn title.getNamespaceId() === 0 ?\n\t\t\t\tmw.msg( 'blanknamespace' ).replace( /^[(]?/, '' ).replace( /[)]?$/, '' ) :\n\t\t\t\ttitle.getNamespacePrefix().replace( /[:]?$/, '' );\n\t\t},\n\n\t\t/**\n\t\t * Outputs time and date of last edit, in the user's language.\n\t\t *\n\t\t * Because we're outputting this per-language, the format will vary (e.g.\n\t\t * in en and en-gb, the placement of the month and day will be swapped).\n\t\t * That said, let's standardize on 24-hour time since that's what users\n\t\t * are used to (and it's cleaner than having to add AM/PM).\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tlastEdited: function () {\n\t\t\tvar date = new Date( this.timestamp ),\n\t\t\t\ttimeString,\n\t\t\t\tdateString;\n\n\t\t\tif ( date instanceof Date ) {\n\t\t\t\ttimeString = date.toLocaleString( userLanguage, {\n\t\t\t\t\ttimeZone: 'UTC',\n\t\t\t\t\thour: 'numeric',\n\t\t\t\t\tminute: 'numeric',\n\t\t\t\t\thour12: false\n\t\t\t\t} );\n\t\t\t\tdateString = date.toLocaleString( userLanguage, {\n\t\t\t\t\ttimeZone: 'UTC',\n\t\t\t\t\tyear: 'numeric',\n\t\t\t\t\tmonth: 'long',\n\t\t\t\t\tday: 'numeric'\n\t\t\t\t} );\n\n\t\t\t\treturn timeString + ', ' + dateString;\n\t\t\t}\n\n\t\t\treturn '';\n\t\t}\n\t}\n};\n</script>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/components/results/VideoResult.vue","messages":[{"ruleId":"max-len","severity":1,"message":"This line has a length of 123. Maximum allowed is 100.","line":24,"column":1,"nodeType":"Program","messageId":"max","endLine":24,"endColumn":109}],"errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"<template>\n\t<a ref=\"link\"\n\t\tclass=\"wbmi-video-result\"\n\t\t:href=\"canonicalurl\"\n\t\ttarget=\"_blank\"\n\t\t:title=\"title\"\n\t\t@click=\"showDetails\">\n\n\t\t<img\n\t\t\t:src=\"thumbnail\"\n\t\t\t:alt=\"displayName\"\n\t\t\tclass=\"wbmi-video-result__thumbnail\"\n\t\t\tloading=\"lazy\"\n\t\t>\n\n\t\t<div class=\"wbmi-video-result__body\">\n\t\t\t<h3 class=\"wbmi-video-result__title\">\n\t\t\t\t{{ displayName }}\n\t\t\t</h3>\n\n\t\t\t<h4 class=\"wbmi-video-result__meta\">\n\t\t\t\t<span class=\"wbmi-video-result__duration\">\n\t\t\t\t\t<wbmi-icon :icon=\"icon\"></wbmi-icon>\n\t\t\t\t\t<span v-if=\"formattedDuration\" class=\"wbmi-video-result__duration__text\">{{ formattedDuration }}</span>\n\t\t\t\t</span>\n\t\t\t\t<span v-if=\"mime\" class=\"wbmi-video-result__mime\">\n\t\t\t\t\t{{ mime }}\n\t\t\t\t</span>\n\t\t\t</h4>\n\t\t</div>\n\t</a>\n</template>\n\n<script>\n/**\n * @file VideoResult.vue\n *\n * Video-specific search result layout. Implements the general searchResult\n * mixin as well as the \"time-based\" result mixin. Also includes custom\n * computed properties for resolution and mime type.\n */\nvar searchResult = require( '../../mixins/searchResult.js' ),\n\tsearchResultTimeBased = require( '../../mixins/searchResultTimeBased.js' ),\n\tWbmiIcon = require( '../base/Icon.vue' ),\n\ticons = require( '../../../../lib/icons.js' );\n\n// @vue/component\nmodule.exports = {\n\tname: 'VideoResult',\n\n\tcomponents: {\n\t\t'wbmi-icon': WbmiIcon\n\t},\n\n\tmixins: [\n\t\tsearchResult,\n\t\tsearchResultTimeBased\n\t],\n\n\tinheritAttrs: false,\n\n\tdata: function () {\n\t\treturn {\n\t\t\ticon: icons.wbmiIconPlay\n\t\t};\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * @return {string}\n\t\t */\n\t\tresolution: function () {\n\t\t\tvar width = this.imageinfo[ 0 ].width,\n\t\t\t\theight = this.imageinfo[ 0 ].height;\n\n\t\t\treturn width + 'x' + height;\n\t\t}\n\t}\n};\n</script>\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/init.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/mixins/autocompleteLookupHandler.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/mixins/searchResult.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/mixins/searchResultTimeBased.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/models/SearchFilter.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/plugins/eventLogger.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/store/actions.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/store/getters.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/store/index.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/store/mutations.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediasearch-vue/store/state.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/mediawiki.template.mustache+dom.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/polyfills/Array.prototype.find.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/polyfills/Array.prototype.findIndex.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/search/PropertySuggestionsWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/search/index.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/serialization/MediaInfoDeserializer.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/AddPropertyWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/ConstraintsReportHandlerElement.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/ItemWidget.js","messages":[{"ruleId":"compat/compat","severity":2,"message":"MutationObserver.observe() is not supported in Safari 5.1","line":448,"column":4,"nodeType":"MemberExpression","endLine":452,"endColumn":15},{"ruleId":"compat/compat","severity":2,"message":"MutationObserver is not supported in Safari 5.1, iOS Safari 6.0-6.1","line":448,"column":4,"nodeType":"NewExpression","endLine":452,"endColumn":7}],"errorCount":2,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use strict';\n\n/**\n * @constructor\n * @param {Object} config Configuration options\n * @param {Object} config.qualifiers Qualifiers map: { propertyId: datatype, ...}\n * @param {string} config.entityId Entity ID (e.g. M123)\n * @param {string} config.propertyId Property ID (e.g. P123 id of `depicts` property)\n * @param {string} [config.guid] GUID of existing statement, or null for new\n * @param {number} [config.rank] One of datamodel.Statement.RANK.*\n * @param {string} [config.snakType] value, somevalue, or novalue\n * @param {dataValues.DataValue} [config.dataValue] Relevant DataValue object, or null for valueless\n * @param {string} [config.editing] True for edit mode, False for read mode\n */\nvar DATA_TYPES,\n\tQualifierWidget = require( './QualifierWidget.js' ),\n\tConstraintsReportHandlerElement = require( './ConstraintsReportHandlerElement.js' ),\n\tComponentWidget = require( 'wikibase.mediainfo.base' ).ComponentWidget,\n\tDOMLessGroupWidget = require( 'wikibase.mediainfo.base' ).DOMLessGroupWidget,\n\tFormatValueElement = require( 'wikibase.mediainfo.base' ).FormatValueElement,\n\tGlobeCoordinateInputWidget = require( './inputs/GlobeCoordinateInputWidget.js' ),\n\tdatamodel = require( 'wikibase.datamodel' ),\n\tvalueTypes = {\n\t\tVALUE: datamodel.PropertyValueSnak.TYPE,\n\t\tSOMEVALUE: datamodel.PropertySomeValueSnak.TYPE,\n\t\tNOVALUE: datamodel.PropertyNoValueSnak.TYPE\n\t},\n\tkartoBox,\n\tkartoEditing,\n\tItemWidget;\n\n/**\n * Enum for data types that have special display requirements.\n */\nDATA_TYPES = {\n\tGLOBECOORDINATE: 'globecoordinate'\n\t// NOTE: when the need arises to put more datatype-specific logic\n\t// in here, consider refactoring this similar to the input fields\n};\n\n/**\n * @param {Object} config Configuration options\n */\nItemWidget = function MediaInfoStatementsItemWidget( config ) {\n\tconfig = config || {};\n\n\tthis.guidGenerator = new wikibase.utilities.ClaimGuidGenerator( config.entityId );\n\n\t// set these first - the parent constructor could call other methods\n\t// (e.g. setDisabled) which may cause a re-render, and will need\n\t// some of these...\n\tthis.state = {\n\t\tediting: !!config.editing,\n\t\tpropertyId: config.propertyId,\n\t\tguid: config.guid || this.guidGenerator.newGuid(),\n\t\trank: config.rank || datamodel.Statement.RANK.NORMAL,\n\t\tsnakType: config.snakType || valueTypes.NOVALUE,\n\t\tdataValue: config.dataValue || null,\n\t\tkartographer: false,\n\t\tconstraintsReport: null\n\t};\n\n\t// Coordinate values are displayed with an additional element, an interactive map;\n\t// Determine if we are dealing with one and initialize the map UI here if so.\n\t// Attempt to load Kartographer dependencies if we are dealing with coordinates\n\tthis.$map = $( '<div>' ).addClass( 'wbmi-item__map' );\n\tthis.map = undefined;\n\tthis.initializeMap();\n\n\tItemWidget.parent.call( this, $.extend( {}, config ) );\n\tDOMLessGroupWidget.call( this, $.extend( {}, config ) );\n\tComponentWidget.call(\n\t\tthis,\n\t\t'wikibase.mediainfo.statements',\n\t\t'templates/statements/ItemWidget.mustache+dom'\n\t);\n\tFormatValueElement.call( this, $.extend( {}, config ) );\n\tConstraintsReportHandlerElement.call( this, $.extend( {}, config ) );\n};\n\nOO.inheritClass( ItemWidget, OO.ui.Widget );\nOO.mixinClass( ItemWidget, DOMLessGroupWidget );\nOO.mixinClass( ItemWidget, ComponentWidget );\nOO.mixinClass( ItemWidget, FormatValueElement );\nOO.mixinClass( ItemWidget, ConstraintsReportHandlerElement );\n\n/**\n * @inheritDoc\n */\nItemWidget.prototype.getTemplateData = function () {\n\tvar self = this,\n\t\tlabelPromise,\n\t\terrors = this.getErrors(),\n\t\terrorMessages = ( errors.length > 0 ) ?\n\t\t\terrors.map( function ( error ) {\n\t\t\t\treturn new OO.ui.MessageWidget( {\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tlabel: error,\n\t\t\t\t\tclasses: [ 'wbmi-statement-error-msg--inline' ]\n\t\t\t\t} );\n\t\t\t} ) : null;\n\n\t// Get the formatted label text for the value if necessary,\n\t// or else use a dummy promise\n\t// Determine if we are dealing with a globecoordinate value, which has\n\t// special display needs\n\tif ( this.state.dataValue ) {\n\t\tlabelPromise = this.formatValue( this.state.dataValue, 'text/html', null, this.state.propertyId );\n\t} else {\n\t\tlabelPromise = $.Deferred().resolve(\n\t\t\tmw.message(\n\t\t\t\tthis.state.snakType === valueTypes.SOMEVALUE ?\n\t\t\t\t\t'wikibasemediainfo-filepage-statement-some-value' :\n\t\t\t\t\t'wikibasemediainfo-filepage-statement-no-value'\n\t\t\t).parse()\n\t\t).promise();\n\t}\n\n\treturn labelPromise.then( function ( label ) {\n\t\tvar id = self.dataValue ? self.dataValue.toJSON().id : '',\n\t\t\tprominent = self.state.rank === datamodel.Statement.RANK.PREFERRED,\n\t\t\tdataValueType = self.state.dataValue ? self.state.dataValue.getType() : undefined,\n\t\t\tremoveButton,\n\t\t\taddQualifierButton,\n\t\t\tformatResponse;\n\n\t\tformatResponse = function ( html ) {\n\t\t\treturn $( '<div>' )\n\t\t\t\t.append( html )\n\t\t\t\t.find( 'a' )\n\t\t\t\t.attr( 'target', '_blank' )\n\t\t\t\t.end()\n\t\t\t\t.html();\n\t\t};\n\n\t\tremoveButton = new OO.ui.ButtonWidget( {\n\t\t\tclasses: [ 'wbmi-item-remove' ],\n\t\t\ttitle: mw.message( 'wikibasemediainfo-statements-item-remove' ).text(),\n\t\t\tflags: 'destructive',\n\t\t\ticon: 'trash',\n\t\t\tframed: false\n\t\t} );\n\t\tremoveButton.connect( self, { click: [ 'emit', 'delete' ] } );\n\n\t\taddQualifierButton = new OO.ui.ButtonWidget( {\n\t\t\tclasses: [ 'wbmi-item-qualifier-add' ],\n\t\t\tlabel: mw.message( 'wikibasemediainfo-statements-item-add-qualifier' ).text(),\n\t\t\tflags: 'progressive',\n\t\t\tframed: false\n\t\t} );\n\t\taddQualifierButton.connect( self, { click: [ 'addQualifier' ] } );\n\n\t\treturn {\n\t\t\terrors: errorMessages,\n\t\t\tediting: self.state.editing,\n\t\t\tqualifiers: self.getItems(),\n\t\t\tlabel: formatResponse( label ),\n\t\t\tid: id.replace( /^.+:/, '' ),\n\t\t\tprominent: prominent,\n\t\t\tprominenceMessage: prominent ?\n\t\t\t\tmw.message( 'wikibasemediainfo-statements-item-is-prominent' ).text() :\n\t\t\t\tmw.message( 'wikibasemediainfo-statements-item-mark-as-prominent' ).text(),\n\t\t\tprominenceToggleHandler: self.toggleItemProminence.bind( self ),\n\t\t\tremoveButton: removeButton,\n\t\t\taddQualifierButton: addQualifierButton,\n\t\t\tisGlobecoordinate: dataValueType === DATA_TYPES.GLOBECOORDINATE,\n\t\t\tkartographer: self.state.kartographer,\n\t\t\tmap: self.$map,\n\t\t\tconstraintsReport: self.state.constraintsReport &&\n\t\t\t\tself.popupFromResults( self.state.constraintsReport )\n\t\t};\n\t} );\n};\n\nItemWidget.prototype.render = function () {\n\tvar self = this,\n\t\tpromise = ComponentWidget.prototype.render.call( this );\n\n\tif (\n\t\tthis.map &&\n\t\tthis.state.dataValue &&\n\t\tthis.state.dataValue.getType() === DATA_TYPES.GLOBECOORDINATE\n\t) {\n\t\tpromise = promise.then( function ( $element ) {\n\t\t\tvar data = self.state.dataValue.getValue(),\n\t\t\t\tlayer = kartoEditing.getKartographerLayer( self.map );\n\n\t\t\t// we've just rerendered & DOM might look different then it did\n\t\t\t// before, when the map size was initially calculated\n\t\t\tself.map.invalidateSize();\n\n\t\t\tlayer.setGeoJSON( {\n\t\t\t\ttype: 'Feature',\n\t\t\t\tproperties: {},\n\t\t\t\tgeometry: {\n\t\t\t\t\ttype: 'Point',\n\t\t\t\t\tcoordinates: [\n\t\t\t\t\t\tdata.getLongitude(),\n\t\t\t\t\t\tdata.getLatitude()\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t/* eslint-disable no-undef */\n\t\t\tself.map.setView(\n\t\t\t\tL.latLng( data.getLatitude(), data.getLongitude() ),\n\t\t\t\tGlobeCoordinateInputWidget.precisionToZoom( data.getPrecision(), data.getLatitude() )\n\t\t\t);\n\t\t\t/* eslint-enable no-undef */\n\n\t\t\treturn $element;\n\t\t} );\n\t}\n\n\treturn promise;\n};\n\n/**\n * @param {Object} event\n * @return {jQuery.Promise}\n */\nItemWidget.prototype.toggleItemProminence = function ( event ) {\n\tevent.preventDefault();\n\n\tif ( this.isDisabled() ) {\n\t\treturn $.Deferred().resolve( this.$element ).promise();\n\t}\n\n\treturn this.setState( {\n\t\trank: this.state.rank === datamodel.Statement.RANK.PREFERRED ?\n\t\t\tdatamodel.Statement.RANK.NORMAL :\n\t\t\tdatamodel.Statement.RANK.PREFERRED\n\t} ).then( this.emit.bind( this, 'change' ) );\n};\n\n/**\n * @param {datamodel.Snak|undefined} [data]\n * @return {QualifierWidget}\n */\nItemWidget.prototype.createQualifier = function ( data ) {\n\tvar widget = new QualifierWidget( { editing: this.state.editing } ),\n\t\tpromise = $.Deferred().resolve().promise(),\n\t\tself = this;\n\n\tif ( data ) {\n\t\tpromise = widget.setData( data );\n\t}\n\n\treturn promise.then(\n\t\tfunction () {\n\t\t\twidget.connect( self, { delete: [ 'removeItems', [ widget ] ] } );\n\t\t\twidget.connect( self, { delete: [ 'emit', 'change' ] } );\n\t\t\twidget.connect( self, { change: [ 'emit', 'change' ] } );\n\n\t\t\treturn widget;\n\t\t}\n\t);\n};\n\n/**\n * @param {datamodel.Snak|undefined} data\n */\nItemWidget.prototype.addQualifier = function ( data ) {\n\tvar self = this;\n\tthis.createQualifier( data ).then( function ( widget ) {\n\t\tself.addItems( [ widget ] );\n\t\tself.emit( 'change' );\n\t\tself.render().then( widget.focus.bind( widget ) );\n\t} );\n};\n\n/**\n * @param {boolean} editing\n * @return {jQuery.Promise}\n */\nItemWidget.prototype.setEditing = function ( editing ) {\n\tvar promises = this.getItems().map( function ( widget ) {\n\t\treturn widget.setEditing( editing );\n\t} );\n\n\treturn $.when.apply( $, promises ).then( this.setState.bind( this, { editing: editing } ) );\n};\n\n/**\n * @return {datamodel.Statement}\n */\nItemWidget.prototype.getData = function () {\n\tvar snak;\n\n\tswitch ( this.state.snakType ) {\n\t\tcase valueTypes.SOMEVALUE:\n\t\t\tsnak = new datamodel.PropertySomeValueSnak( this.state.propertyId );\n\t\t\tbreak;\n\t\tcase valueTypes.NOVALUE:\n\t\t\tsnak = new datamodel.PropertyNoValueSnak( this.state.propertyId );\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tsnak = this.state.dataValue ?\n\t\t\t\tnew datamodel.PropertyValueSnak( this.state.propertyId, this.state.dataValue, null ) :\n\t\t\t\tnew datamodel.PropertyNoValueSnak( this.state.propertyId );\n\t\t\tbreak;\n\t}\n\n\treturn new datamodel.Statement(\n\t\tnew datamodel.Claim(\n\t\t\tsnak,\n\t\t\tnew datamodel.SnakList( this.getItems()\n\t\t\t\t.map( function ( item ) {\n\t\t\t\t\t// try to fetch data - if it fails (likely because of incomplete input),\n\t\t\t\t\t// we'll just ignore that qualifier\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn item.getData();\n\t\t\t\t\t} catch ( e ) {\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t} )\n\t\t\t\t.filter( function ( data ) {\n\t\t\t\t\treturn data instanceof datamodel.Snak;\n\t\t\t\t} )\n\t\t\t),\n\t\t\tthis.state.guid\n\t\t),\n\t\tnull,\n\t\tthis.state.rank\n\t);\n};\n\n/**\n * @param {datamodel.Statement} data\n * @return {jQuery.Deferred}\n */\nItemWidget.prototype.setData = function ( data ) {\n\tvar self = this,\n\t\texisting = {},\n\t\tpromises = [],\n\t\tclaim,\n\t\tmainSnak,\n\t\tqualifiers,\n\t\ttype;\n\n\t// Bail early and discard existing data if data argument is not a snak\n\tif ( !( data instanceof datamodel.Statement ) ) {\n\t\tthrow new Error( 'Invalid statement' );\n\t}\n\n\t// Store the attributes we need to reference frequently for later use\n\tclaim = data.getClaim();\n\tmainSnak = claim.getMainSnak();\n\tqualifiers = claim.getQualifiers();\n\ttype = mainSnak.getType();\n\n\t// get rid of existing widgets that are no longer present in the\n\t// new set of data we've been fed (or are in an invalid state)\n\tthis.removeItems( this.getItems().filter( function ( item ) {\n\t\tvar qualifier;\n\t\ttry {\n\t\t\tqualifier = item.getData();\n\t\t} catch ( e ) {\n\t\t\t// failed to fetch data (likely because of incomplete input),\n\t\t\t// so we should remove this qualifier...\n\t\t\treturn true;\n\t\t}\n\t\treturn !qualifiers.hasItem( qualifier );\n\t} ) );\n\n\t// figure out which items have an existing widget already\n\t// we're doing this outside of the creation below, because\n\t// setData is async, and new objects may not immediately\n\t// have their data set\n\tqualifiers.each( function ( i, qualifier ) {\n\t\texisting[ i ] = self.findItemFromData( qualifier );\n\t} );\n\n\t// add new qualifiers that don't already exist\n\tqualifiers.each( function ( i, qualifier ) {\n\t\tvar widget = existing[ i ], widgetPromise;\n\t\tif ( widget !== null ) {\n\t\t\tself.moveItem( widget, i );\n\t\t\tpromises.push( widget.setData( qualifier ) );\n\t\t} else {\n\t\t\twidgetPromise = self.createQualifier()\n\t\t\t\t.then( function ( innerWidget ) {\n\t\t\t\t\tself.insertItem( innerWidget, i );\n\t\t\t\t\treturn innerWidget.setData( qualifier );\n\t\t\t\t} );\n\t\t\tpromises.push( widgetPromise );\n\t\t}\n\t} );\n\n\treturn $.when.apply( $, promises )\n\t\t.then( this.setState.bind( this, {\n\t\t\tpropertyId: mainSnak.getPropertyId(),\n\t\t\tguid: claim.getGuid() || this.guidGenerator.newGuid(),\n\t\t\trank: data.getRank(),\n\t\t\tsnakType: type,\n\t\t\tdataValue: type === valueTypes.VALUE ? mainSnak.getValue() : null,\n\t\t\t// if new data was passed in, error is no longer valid\n\t\t\terrors: data.equals( this.getData() ) ? this.getErrors() : []\n\t\t} ) )\n\t\t.then( this.initializeMap.bind( this ) );\n};\n\n/**\n * @return {jQuery.Promise}\n */\nItemWidget.prototype.initializeMap = function () {\n\tvar self = this;\n\n\tif (\n\t\t// map already initialized previously\n\t\tthis.map ||\n\t\t// not a geocoordinate datavalue = no map needed\n\t\t!this.state.dataValue ||\n\t\tthis.state.dataValue.getType() !== DATA_TYPES.GLOBECOORDINATE\n\t) {\n\t\treturn this.setState( {} );\n\t}\n\n\treturn mw.loader.using( [ 'ext.kartographer.box', 'ext.kartographer.editing' ] )\n\t\t.then( function ( require ) {\n\t\t\tvar sdTab;\n\n\t\t\tkartoBox = require( 'ext.kartographer.box' );\n\t\t\tkartoEditing = require( 'ext.kartographer.editing' );\n\n\t\t\t// Unlike in the GlobeCoordinateInputWidget, the ItemWidget's map does not\n\t\t\t// enforce maxBounds. This is because Wikidata allows users to input\n\t\t\t// coordinates like 40N, 250E; if a user has entered such a value manually,\n\t\t\t// let's do our best to display it. Leaflet will just wrap around the\n\t\t\t// virtual \"globe\" to display the marker.\n\t\t\tself.map = kartoBox.map( {\n\t\t\t\tcontainer: self.$map[ 0 ],\n\t\t\t\tcenter: [ 20, 0 ],\n\t\t\t\tzoom: 2,\n\t\t\t\tallowFullScreen: false,\n\t\t\t\tminZoom: 1\n\t\t\t} );\n\n\t\t\t// Because maps within ItemWidgets need to be immediately visible in \"read\n\t\t\t// mode\", we need a MutationObserver which can watch for when the structured\n\t\t\t// data tab (which is hidden by default) becomes visible so that the map can\n\t\t\t// be forced to recalculate its size. Leaflet cannot correctly determine\n\t\t\t// the size of the map when it is initialized on a hidden element (like\n\t\t\t// the children of a non-visible tab), so we must do it again when the map\n\t\t\t// becomes visible.\n\t\t\t// eslint-disable-next-line no-jquery/no-global-selector\n\t\t\tsdTab = $( '.wbmi-structured-data-header' ).closest( '.wbmi-tab' )[ 0 ];\n\t\t\tnew MutationObserver( function () {\n\t\t\t\tif ( self.$map.parents( 'body' ).length > 0 ) {\n\t\t\t\t\tself.map.invalidateSize();\n\t\t\t\t}\n\t\t\t} ).observe( sdTab, {\n\t\t\t\tattributes: true,\n\t\t\t\tattributeFilter: [ 'aria-hidden' ]\n\t\t\t} );\n\n\t\t\treturn self.setState( { kartographer: true } );\n\t\t} );\n};\n\n/**\n * Handle the part of the response from a wbcheckconstraints api call that is relevant to this\n * ItemWidget's property id & extract the constraint check results from the part of API\n * response that is relevant to this StatementWidget's propertyId\n *\n * @see WikibaseQualityConstraints/modules/gadget.js::_extractResultsForStatement()\n * @param {Object|null} results\n * @return {jQuery.Promise}\n */\nItemWidget.prototype.setConstraintsReport = function ( results ) {\n\tvar promises = [];\n\n\t// extract qualifier constraint reports, pass them along to qualifier widget,\n\t// and gather promises\n\tpromises = this.getItems().map( function ( qualifierWidget ) {\n\t\tvar data, propertyId, hash, result;\n\n\t\ttry {\n\t\t\tdata = qualifierWidget.getData();\n\t\t\tpropertyId = data.getPropertyId();\n\t\t\thash = data.getHash();\n\n\t\t\tresult = results.qualifiers[ propertyId ].filter( function ( responseForQualifier ) {\n\t\t\t\treturn responseForQualifier.hash === hash;\n\t\t\t} )[ 0 ] || null;\n\t\t\treturn qualifierWidget.setConstraintsReport( result );\n\t\t} catch ( e ) {\n\t\t\treturn qualifierWidget.setConstraintsReport( null );\n\t\t}\n\t} );\n\n\t// update own constraint report & add to list of promises\n\tpromises = promises.concat( this.setState( { constraintsReport: results && results.mainsnak.results } ) );\n\n\t// return promise that doesn't resolve until all constraints reports have been rendered\n\treturn $.when.apply( $, promises );\n};\n\nmodule.exports = ItemWidget;\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/LinkNoticeWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/QualifierWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/StatementWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/config/index.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/config/wbMonolingualTextLanguages.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/config/wbTermsLanguages.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/index.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/inputs/AbstractInputWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/inputs/EntityAutocompleteInputWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/inputs/EntityInputWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/inputs/GlobeCoordinateInputWidget.js","messages":[{"ruleId":"compat/compat","severity":2,"message":"MutationObserver.observe() is not supported in Safari 5.1","line":426,"column":4,"nodeType":"MemberExpression","endLine":434,"endColumn":15},{"ruleId":"compat/compat","severity":2,"message":"MutationObserver is not supported in Safari 5.1, iOS Safari 6.0-6.1","line":426,"column":4,"nodeType":"NewExpression","endLine":434,"endColumn":7}],"errorCount":2,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use strict';\n\nvar ComponentWidget = require( 'wikibase.mediainfo.base' ).ComponentWidget,\n\tAbstractInputWidget = require( './AbstractInputWidget.js' ),\n\tkartoBox,\n\tkartoEditing,\n\tGlobeCoordinateInputWidget;\n\n/**\n * Widget that wraps globe-coordinate fields.\n *\n * @param {Object} config Configuration options\n * @param {boolean} [config.isQualifier]\n */\nGlobeCoordinateInputWidget = function MediaInfoStatementsGlobeCoordinateInputWidget( config ) {\n\tvar self = this;\n\n\tconfig = config || {};\n\n\tthis.state = {\n\t\tlatitude: null,\n\t\tlongitude: null,\n\t\t// precision will be inferred from input, but can be manually overridden;\n\t\t// as soon as it changes manually, the custom precision will be used\n\t\tinferredPrecision: null,\n\t\tcustomPrecision: null,\n\t\tisQualifier: !!config.isQualifier,\n\t\tkartographer: false,\n\t\texpanded: false\n\t};\n\n\tthis.parseValuePromise = undefined;\n\tthis.debouncedOnChange = OO.ui.debounce( this.onChange.bind( this ), 200 );\n\tthis.onMapClickHandler = this.onMapClick.bind( this );\n\n\tthis.coordinateInput = new OO.ui.TextInputWidget( {\n\t\tclasses: [ 'wbmi-input-widget__input' ],\n\t\tplaceholder: mw.message( 'wikibasemediainfo-coordinate-input-placeholder' ).text(),\n\t\tisRequired: true,\n\t\ttype: 'string',\n\t\tvalidate: function ( value ) {\n\t\t\t// mark input field as invalid except\n\t\t\treturn value === '' || self.hasValidInput();\n\t\t}\n\t} );\n\n\tthis.precisionInput = new OO.ui.DropdownInputWidget( {\n\t\tclasses: [ 'wbmi-input-widget__input' ],\n\t\tlabel: mw.message( 'wikibasemediainfo-select-precision-label' ).text(),\n\t\toptions: this.getPrecisionOptions(),\n\t\tisRequired: true,\n\t\t$overlay: true\n\t} );\n\n\t// Set up map element for Kartographer\n\tthis.$map = $( '<div>' ).addClass( 'wbmi-input-widget__map' );\n\tthis.map = undefined;\n\tthis.initializeMap();\n\n\tthis.bindEventListeners();\n\n\tGlobeCoordinateInputWidget.parent.call( this );\n\tComponentWidget.call(\n\t\tthis,\n\t\t'wikibase.mediainfo.statements',\n\t\t'templates/statements/inputs/GlobeCoordinateInputWidget.mustache+dom'\n\t);\n};\n\nOO.inheritClass( GlobeCoordinateInputWidget, OO.ui.Widget );\nOO.mixinClass( GlobeCoordinateInputWidget, AbstractInputWidget );\nOO.mixinClass( GlobeCoordinateInputWidget, ComponentWidget );\n\n/**\n * Bind event listeners, including one on the map if using Kartographer\n */\nGlobeCoordinateInputWidget.prototype.bindEventListeners = function () {\n\tvar self = this;\n\n\tthis.coordinateInput.connect( this, { change: this.debouncedOnChange } );\n\tthis.coordinateInput.connect( this, { enter: 'onEnter' } );\n\tthis.precisionInput.connect( this, { change: 'onPrecisionChange' } );\n\tmw.loader.using( [ 'ext.kartographer.box', 'ext.kartographer.editing' ] )\n\t\t.then( function () {\n\t\t\tself.map.on( 'click', self.onMapClickHandler );\n\t\t} );\n};\n\n/**\n * Unbind event listeners, including one on the map if using Kartographer\n */\nGlobeCoordinateInputWidget.prototype.unbindEventListeners = function () {\n\tvar self = this;\n\n\tthis.coordinateInput.disconnect( this, { change: this.debouncedOnChange } );\n\tthis.coordinateInput.disconnect( this, { enter: 'onEnter' } );\n\tthis.precisionInput.disconnect( this, { change: 'onPrecisionChange' } );\n\n\tmw.loader.using( [ 'ext.kartographer.box', 'ext.kartographer.editing' ] )\n\t\t.then( function () {\n\t\t\tself.map.off( 'click', self.onMapClickHandler );\n\t\t} );\n};\n\n/**\n * Set the data of this widget programatically. This method is used to populate\n * qualifier-mode coordinate inputs with pre-existing data.\n *\n * @param {dataValues.DataValue} newData\n * @return {jQuery.Promise}\n */\nGlobeCoordinateInputWidget.prototype.setData = function ( newData ) {\n\tvar json = newData.toJSON(),\n\t\tself = this,\n\t\texistingData;\n\n\ttry {\n\t\texistingData = this.getData();\n\t} catch ( e ) {\n\t\t// no existing data, proceed\n\t}\n\n\tthis.unbindEventListeners();\n\tthis.coordinateInput.setValue( json.latitude + ', ' + json.longitude );\n\tthis.precisionInput.setValue( json.precision );\n\tthis.bindEventListeners();\n\n\treturn this.setState( {\n\t\tlatitude: json.latitude,\n\t\tlongitude: json.longitude,\n\t\tinferredPrecision: json.precision,\n\t\tcustomPrecision: null,\n\t\texpanded: false\n\t} ).then( function ( $element ) {\n\t\tif ( !newData.equals( existingData ) ) {\n\t\t\tself.emit( 'change', self );\n\t\t}\n\t\treturn $element;\n\t} );\n};\n\n/**\n * @inheritDoc\n */\nGlobeCoordinateInputWidget.prototype.getData = function () {\n\tif ( this.state.latitude === null || this.state.longitude === null ) {\n\t\tthrow new Error( 'No valid coordinate' );\n\t}\n\n\treturn dataValues.newDataValue( 'globecoordinate', {\n\t\tlatitude: this.state.latitude,\n\t\tlongitude: this.state.longitude,\n\t\tprecision: this.state.customPrecision || this.state.inferredPrecision\n\t} );\n};\n\n/**\n * @inheritDoc\n */\nGlobeCoordinateInputWidget.prototype.getRawValue = function () {\n\treturn this.coordinateInput.getValue();\n};\n\n/**\n * @inheritDoc\n */\nGlobeCoordinateInputWidget.prototype.getRawValueOptions = function () {\n\treturn {\n\t\tprecision: this.state.customPrecision || undefined\n\t};\n};\n\n/**\n * @inheritdoc\n */\nGlobeCoordinateInputWidget.prototype.clear = function () {\n\tvar layer;\n\n\tthis.coordinateInput.setValue( '' );\n\tthis.precisionInput.setValue( '' );\n\tthis.coordinateInput.setValidityFlag( true );\n\n\tif ( kartoEditing && this.map ) {\n\t\tlayer = kartoEditing.getKartographerLayer( this.map );\n\t\tlayer.clearLayers();\n\t}\n\n\treturn this.setState( {\n\t\tlatitude: null,\n\t\tlongitude: null,\n\t\tinferredPrecision: null,\n\t\tcustomPrecision: null,\n\t\texpanded: false\n\t} );\n};\n\n/**\n * @param {string} newValue new input value\n */\nGlobeCoordinateInputWidget.prototype.onChange = function ( newValue ) {\n\tvar self = this;\n\n\tif ( this.parseValuePromise && this.parseValuePromise.abort ) {\n\t\tthis.parseValuePromise.abort();\n\t}\n\n\t// If user is simply deleting the previous value, no need to parse;\n\t// Reset values to null and return\n\tif ( newValue === '' ) {\n\t\tthis.setState( {\n\t\t\tlatitude: null,\n\t\t\tlongitude: null,\n\t\t\tinferredPrecision: null\n\t\t} ).then( this.emit.bind( this, 'change', this ) );\n\n\t\treturn;\n\t}\n\n\tthis.parseValuePromise = this.parseValue( undefined, 'globe-coordinate' );\n\tthis.parseValuePromise\n\t\t.then( function ( response ) {\n\t\t\tvar json = response.toJSON();\n\n\t\t\t// set the value of the precision dropdown to the inferred precision if\n\t\t\t// no custom precision has been set\n\t\t\tif ( !self.state.customPrecision ) {\n\t\t\t\tself.unbindEventListeners();\n\t\t\t\tself.precisionInput.setValue( json.precision );\n\t\t\t\tself.bindEventListeners();\n\t\t\t}\n\n\t\t\tself.coordinateInput.setValidityFlag( true );\n\t\t\tself.emit( 'change', self );\n\n\t\t\treturn self.setState( {\n\t\t\t\tlatitude: json.latitude,\n\t\t\t\tlongitude: json.longitude,\n\t\t\t\tinferredPrecision: json.precision\n\t\t\t} );\n\t\t} )\n\t\t.catch( function () {\n\t\t\tself.coordinateInput.setValidityFlag( false );\n\t\t} );\n};\n\nGlobeCoordinateInputWidget.prototype.onPrecisionChange = function () {\n\tthis.setState( {\n\t\tcustomPrecision: Number( this.precisionInput.getValue() )\n\t} ).then( this.emit.bind( this, 'change', this ) );\n};\n\nGlobeCoordinateInputWidget.prototype.onEnter = function () {\n\tif ( this.hasValidInput() ) {\n\t\tthis.emit( 'add', this );\n\t}\n};\n\nGlobeCoordinateInputWidget.prototype.onExpandClick = function () {\n\t// Toggle the map visibility\n\tthis.setState( { expanded: !( this.state.expanded ) } );\n};\n\n/**\n * @param {Object} e event\n */\nGlobeCoordinateInputWidget.prototype.onMapClick = function ( e ) {\n\tvar coordinates = this.map.mouseEventToLatLng( e.originalEvent ),\n\t\tprecision = this.constructor.zoomToPrecision( this.map.getZoom(), coordinates.lat ),\n\t\tmeaningfulDigits = this.constructor.precisionToDigits( precision ),\n\t\tlat = coordinates.lat.toFixed( meaningfulDigits ),\n\t\tlng = coordinates.lng.toFixed( meaningfulDigits ),\n\t\tlatLngStr = lat + ', ' + lng;\n\n\tthis.unbindEventListeners();\n\tthis.coordinateInput.setValue( latLngStr );\n\tthis.precisionInput.setValue( String( precision ) );\n\tthis.bindEventListeners();\n\n\tthis.coordinateInput.setValidityFlag( true );\n\n\tthis.setState( {\n\t\tlatitude: parseFloat( lat ),\n\t\tlongitude: parseFloat( lng ),\n\t\tinferredPrecision: precision\n\t} ).then( this.emit.bind( this, 'change', this ) );\n};\n\n/**\n * @return {boolean}\n */\nGlobeCoordinateInputWidget.prototype.hasValidInput = function () {\n\ttry {\n\t\tthis.getData();\n\t\treturn true;\n\t} catch ( e ) {\n\t\treturn false;\n\t}\n};\n\n/**\n * @inheritDoc\n */\nGlobeCoordinateInputWidget.prototype.getTemplateData = function () {\n\tvar submitButton = new OO.ui.ButtonWidget( {\n\t\t\tclasses: [ 'wbmi-input-widget__button' ],\n\t\t\tlabel: mw.message( 'wikibasemediainfo-globecoordinate-input-button-text' ).text(),\n\t\t\tflags: [ 'progressive' ],\n\t\t\tdisabled: !this.hasValidInput()\n\t\t} ),\n\t\texpandButton = new OO.ui.ToggleButtonWidget( {\n\t\t\tclasses: [\n\t\t\t\t'wbmi-input-widget__button',\n\t\t\t\t'wbmi-input-widget__button--map-expand'\n\t\t\t],\n\t\t\tlabel: mw.message( 'wikibasemediainfo-globecoordinate-map-button-text' ).text(),\n\t\t\tframed: true,\n\t\t\ticon: 'mapPin',\n\t\t\tvalue: this.state.expanded\n\t\t} );\n\n\tsubmitButton.connect( this, { click: [ 'emit', 'add', this ] } );\n\texpandButton.connect( this, { click: 'onExpandClick' } );\n\n\treturn {\n\t\tisQualifier: this.state.isQualifier,\n\t\tcoordinates: {\n\t\t\tlabel: mw.message( 'wikibasemediainfo-coordinate-input-label' ).text(),\n\t\t\tinput: this.coordinateInput\n\t\t},\n\t\tprecision: {\n\t\t\tlabel: mw.message( 'wikibasemediainfo-precision-input-label' ).text(),\n\t\t\tinput: this.precisionInput\n\t\t},\n\t\tsubmitButton: submitButton,\n\t\texpandButton: expandButton,\n\t\texpanded: this.state.expanded,\n\t\tkartographer: this.state.kartographer,\n\t\tmap: this.$map\n\t};\n};\n\n/**\n * @inheritDoc\n */\nGlobeCoordinateInputWidget.prototype.render = function () {\n\tvar self = this;\n\n\treturn ComponentWidget.prototype.render.call( this ).then( function ( $element ) {\n\t\tvar layer, data;\n\n\t\tif ( self.map === undefined || kartoEditing === undefined ) {\n\t\t\treturn $element;\n\t\t}\n\n\t\t// after having re-rendered our DOM, let's also update the marker on our map\n\t\t// to reflect the current state\n\t\tlayer = kartoEditing.getKartographerLayer( self.map );\n\n\t\ttry {\n\t\t\tdata = self.getData().getValue();\n\n\t\t\tlayer.setGeoJSON( {\n\t\t\t\ttype: 'Feature',\n\t\t\t\tproperties: {},\n\t\t\t\tgeometry: {\n\t\t\t\t\ttype: 'Point',\n\t\t\t\t\tcoordinates: [\n\t\t\t\t\t\tdata.getLongitude(),\n\t\t\t\t\t\tdata.getLatitude()\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t/* eslint-disable no-undef */\n\t\t\tself.map.setView(\n\t\t\t\tL.latLng( data.getLatitude(), data.getLongitude() ),\n\t\t\t\tself.constructor.precisionToZoom( data.getPrecision(), data.getLatitude() )\n\t\t\t);\n\t\t\t/* eslint-enable no-undef */\n\t\t} catch ( e ) {\n\t\t\t// no valid location at this point...\n\t\t\tlayer.clearLayers();\n\t\t}\n\n\t\treturn $element;\n\t} );\n};\n\n/**\n * @return {jQuery.Promise}\n */\nGlobeCoordinateInputWidget.prototype.initializeMap = function () {\n\tvar self = this;\n\n\tif ( this.map ) {\n\t\t// map already initialized previously\n\t\treturn this.setState( {} );\n\t}\n\n\treturn mw.loader.using( [ 'ext.kartographer.box', 'ext.kartographer.editing' ] )\n\t\t.then( function ( require ) {\n\t\t\tkartoBox = require( 'ext.kartographer.box' );\n\t\t\tkartoEditing = require( 'ext.kartographer.editing' );\n\n\t\t\tself.map = kartoBox.map( {\n\t\t\t\tcontainer: self.$map[ 0 ],\n\t\t\t\tcenter: [ 20, 0 ],\n\t\t\t\tzoom: 2,\n\t\t\t\tallowFullScreen: false,\n\t\t\t\t// Prevent users from entering weird values like 40 N, 250 E by picking\n\t\t\t\t// on the map. If users manually enter such values we will allow it\n\t\t\t\t// (wikidata considers it valid) and ItemWidget's map will do its best\n\t\t\t\t// to display such values; enforcing bounds on the map used for input\n\t\t\t\t// should make it harder for such values to be entered accidentally.\n\t\t\t\tmaxBounds: [\n\t\t\t\t\t[ 90, -180 ],\n\t\t\t\t\t[ -90, 180 ]\n\t\t\t\t],\n\t\t\t\tminZoom: 1\n\t\t\t} );\n\n\t\t\t// because the map node we'll be attaching this map to has not yet been\n\t\t\t// added to the DOM, it won't know what size it needs to initialize with...\n\t\t\t// we'll listen for DOM changes and when we discover this node getting\n\t\t\t// added, we'll invalidate its existing (incorrect) size\n\t\t\tnew MutationObserver( function () {\n\t\t\t\tif ( self.$map.parents( 'body' ).length > 0 ) {\n\t\t\t\t\tself.map.invalidateSize();\n\n\t\t\t\t\t// note: we're not going to disconnect the observer, because\n\t\t\t\t\t// toggling read & edit mode is going to repeatedly attach/detach\n\t\t\t\t\t// the map from DOM, causing the same thing over and over\n\t\t\t\t}\n\t\t\t} ).observe( document, { childList: true, subtree: true } );\n\n\t\t\tself.setState( { kartographer: true } );\n\t\t} );\n};\n\n/**\n * @inheritdoc\n */\nGlobeCoordinateInputWidget.prototype.focus = function () {\n\tthis.coordinateInput.focus();\n};\n\n/**\n * @inheritdoc\n */\nGlobeCoordinateInputWidget.prototype.setDisabled = function ( disabled ) {\n\tthis.coordinateInput.setDisabled( disabled );\n\tthis.precisionInput.setDisabled( disabled );\n\tGlobeCoordinateInputWidget.parent.prototype.setDisabled.call( this, disabled );\n};\n\n/**\n * Get a language-specific label for a precision value, if one exists.\n *\n * @param {number} precision\n * @return {string}\n */\nGlobeCoordinateInputWidget.prototype.getPrecisionLabel = function ( precision ) {\n\tvar label,\n\t\tpresets = {};\n\n\tpresets[ mw.message( 'wikibasemediainfo-arcminute-label' ).text() ] = 1 / 60;\n\tpresets[ mw.message( 'wikibasemediainfo-arcsecond-label' ).text() ] = 1 / 3600;\n\tpresets[ mw.message( 'wikibasemediainfo-tenth-of-arcsecond-label' ).text() ] = 1 / 36000;\n\tpresets[ mw.message( 'wikibasemediainfo-hundreth-of-arcsecond-label' ).text() ] = 1 / 360000;\n\tpresets[ mw.message( 'wikibasemediainfo-thousanth-of-arcsecond-label' ).text() ] = 1 / 3600000;\n\n\tfor ( label in presets ) {\n\t\tif ( Math.abs( precision - presets[ label ] ) < 0.000000000001 ) {\n\t\t\treturn label;\n\t\t}\n\t}\n\n\treturn '±' + precision + '°';\n};\n\n/**\n * Return options for our precision DropdownInputWidget.\n *\n * @return {Object[]}\n */\nGlobeCoordinateInputWidget.prototype.getPrecisionOptions = function () {\n\tvar precisions = this.constructor.getPrecisions(),\n\t\tprecisionValues = [],\n\t\tself = this;\n\n\tprecisions.forEach( function ( precision ) {\n\t\tprecisionValues.unshift( {\n\t\t\tdata: precision,\n\t\t\tlabel: self.getPrecisionLabel( precision )\n\t\t} );\n\t} );\n\treturn precisionValues;\n};\n\n/**\n * Return an array of all available precision values.\n *\n * @return {Array}\n */\nGlobeCoordinateInputWidget.getPrecisions = function () {\n\treturn [\n\t\t10,\n\t\t1,\n\t\t0.1,\n\t\t0.01,\n\t\t0.001,\n\t\t0.0001,\n\t\t0.00001,\n\t\t0.000001,\n\t\t1 / 60,\n\t\t1 / 3600,\n\t\t1 / 36000,\n\t\t1 / 360000,\n\t\t1 / 3600000\n\t];\n};\n\n/**\n * Given a latitude & zoom level, this will figure out the precision.\n *\n * When the map is zoomed out, the coordinate that's being selected will be\n * a lot less precise than when it's zoomed in. We can determine the maximum\n * precision and use that value to prefill the precision value, because most\n * average users won't really know what those values mean anyway...\n *\n * @see https://groups.google.com/d/msg/google-maps-js-api-v3/hDRO4oHVSeM/osOYQYXg2oUJ\n *\n * @param {number} zoom\n * @param {number} latitude\n * @return {number}\n */\nGlobeCoordinateInputWidget.zoomToPrecision = function ( zoom, latitude ) {\n\tvar precisions = this.getPrecisions(),\n\t\tmetersPerPx = ( 156543.03392 * Math.cos( ( latitude * Math.PI ) / 180 ) ) / Math.pow( 2, zoom ),\n\t\t// 111.32m = 1 degree at equator, then corrected for latitude\n\t\tdegrees = metersPerPx / ( 111.32 * 1000 * Math.cos( latitude * ( Math.PI / 180 ) ) );\n\n\t// find closest match for the actual precision\n\treturn precisions.reduce( function ( best, value ) {\n\t\treturn Math.abs( value - degrees ) < Math.abs( best - degrees ) ? value : best;\n\t}, Math.max.apply( null, precisions ) );\n};\n\n/**\n * Given a precision (e.g. 0.001), this will return the amount of digits\n * that are still meaningful (e.g. 3 digits) for that precision.\n *\n * E.g. 50.131224596772 makes no sense with a precision of 0.1 - there just\n * isn't enough precision and we might as well round to 50.1 immediately.\n *\n * @param {number} precision\n * @return {number}\n */\nGlobeCoordinateInputWidget.precisionToDigits = function ( precision ) {\n\tvar digits = -1,\n\t\tprevious;\n\n\tdo {\n\t\tprevious = precision;\n\t\tdigits++;\n\t\tprecision %= Math.pow( 1 / 10, digits );\n\t} while ( previous === precision );\n\n\treturn digits;\n};\n\n/**\n * Given a latitude & precision, this will figure out the zoom level.\n *\n * @see https://groups.google.com/d/msg/google-maps-js-api-v3/hDRO4oHVSeM/osOYQYXg2oUJ\n *\n * @param {number} precision\n * @param {number} latitude\n * @return {number}\n */\nGlobeCoordinateInputWidget.precisionToZoom = function ( precision, latitude ) {\n\t// 111.32m = 1 degree at equator, then corrected for latitude\n\tvar metersPerPx = precision * ( 111.32 * 1000 * Math.cos( latitude * ( Math.PI / 180 ) ) ),\n\t\tzoom = Math.log( ( 156543.03392 * Math.cos( ( latitude * Math.PI ) / 180 ) ) / metersPerPx ) / Math.log( 2 );\n\n\treturn Math.round( zoom );\n};\n\nmodule.exports = GlobeCoordinateInputWidget;\n","usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/inputs/MonolingualTextInputWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/inputs/MultiTypeInputWrapperWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/inputs/QuantityInputWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/inputs/StringInputWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/inputs/TimeInputWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/inputs/UnsupportedInputWidget.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/resources/statements/inputs/index.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/README/1.ExampleComponentWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/README/2.TemplatingFeatures.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/README/3.BestPractices.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/base/ComponentWidget.test.js","messages":[{"ruleId":"compat/compat","severity":2,"message":"Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11","line":9,"column":34,"nodeType":"MemberExpression","endLine":9,"endColumn":47}],"errorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use strict';\n\nconst sinon = require( 'sinon' ),\n\thooks = require( '../../support/hooks.js' );\nlet ComponentWidget,\n\tExampleComponentWidget,\n\tsandbox;\n\nQUnit.module( 'ComponentWidget', Object.assign( {}, hooks.mediainfo, {\n\tbeforeEach: function () {\n\t\thooks.mediainfo.beforeEach();\n\n\t\tsandbox = sinon.createSandbox();\n\t\tsandbox.stub( mw.templates, 'get' ).returns( {\n\t\t\t'ExampleComponentWidget.mustache+dom': '<div>{{variable}}</div>'\n\t\t} );\n\n\t\tComponentWidget = require( '../../../../resources/base/ComponentWidget.js' );\n\t\tExampleComponentWidget = function ( config ) {\n\t\t\tconfig = config || {};\n\n\t\t\tthis.state = {\n\t\t\t\tvariable: config.variable || 'foo'\n\t\t\t};\n\n\t\t\tExampleComponentWidget.parent.call( this, config );\n\t\t\tComponentWidget.call(\n\t\t\t\tthis,\n\t\t\t\t'module-not-required-because-stubbed-out',\n\t\t\t\t'ExampleComponentWidget.mustache+dom'\n\t\t\t);\n\t\t};\n\t\tOO.inheritClass( ExampleComponentWidget, OO.ui.Widget );\n\t\tOO.mixinClass( ExampleComponentWidget, ComponentWidget );\n\t},\n\tafterEach: function () {\n\t\tsandbox.restore();\n\t\thooks.mediainfo.afterEach();\n\t}\n} ), function () {\n\tQUnit.test( 'Widget renders with default state', function ( assert ) {\n\t\tconst done = assert.async(),\n\t\t\twidget = new ExampleComponentWidget( { variable: 'foo' } );\n\n\t\twidget.render().then( function ( $element ) {\n\t\t\tassert.strictEqual( $element.text(), 'foo' );\n\t\t\tdone();\n\t\t} );\n\t} );\n\n\tQUnit.test( 'Widget rerenders with new state', function ( assert ) {\n\t\tconst done = assert.async(),\n\t\t\twidget = new ExampleComponentWidget( { variable: 'foo' } );\n\n\t\twidget.setState( { variable: 'bar' } ).then( function ( $element ) {\n\t\t\tassert.strictEqual( $element.text(), 'bar' );\n\t\t\tdone();\n\t\t} );\n\t} );\n\n\tQUnit.test( 'Widget renders with changed template data', function ( assert ) {\n\t\tconst done = assert.async(),\n\t\t\twidget = new ExampleComponentWidget( { variable: 'foo' } );\n\n\t\t// override getTemplateData method to change the params that'll\n\t\t// be fed to the template\n\t\twidget.getTemplateData = function () {\n\t\t\treturn { variable: 'changed' };\n\t\t};\n\n\t\twidget.setState( { variable: 'bar' } ).then( function ( $element ) {\n\t\t\tassert.strictEqual( $element.text(), 'changed' );\n\t\t\tdone();\n\t\t} );\n\t} );\n\n\tQUnit.test( 'Widget renders with async changed template data', function ( assert ) {\n\t\tconst done = assert.async(),\n\t\t\twidget = new ExampleComponentWidget( { variable: 'foo' } );\n\n\t\t// override getTemplateData method to change the params that'll\n\t\t// be fed to the template - that will be a promise that\n\t\t// doesn't resolve immediately, to simulate e.g. an API call\n\t\twidget.getTemplateData = function () {\n\t\t\tconst deferred = $.Deferred();\n\t\t\tsetTimeout( deferred.resolve.bind( deferred, { variable: 'changed async' } ), 10 );\n\t\t\treturn deferred.promise();\n\t\t};\n\n\t\twidget.setState( { variable: 'bar' } ).then( function ( $element ) {\n\t\t\tassert.strictEqual( $element.text(), 'changed async' );\n\t\t\tdone();\n\t\t} );\n\t} );\n\n\tQUnit.test( 'Widget will not rerender on state change if stopped', function ( assert ) {\n\t\tconst done = assert.async(),\n\t\t\twidget = new ExampleComponentWidget( { variable: 'foo' } );\n\n\t\t// override shouldRerender method to instruct component not to rerender\n\t\twidget.shouldRerender = function () {\n\t\t\treturn false;\n\t\t};\n\n\t\twidget.setState( { variable: 'bar' } ).then( function ( $element ) {\n\t\t\tassert.strictEqual( $element.text(), 'foo' );\n\t\t\tdone();\n\t\t} );\n\t} );\n\n\tQUnit.test( 'Widget will only rerender once when multiple state changes happen during previous render', function ( assert ) {\n\t\tconst done = assert.async(),\n\t\t\twidget = new ExampleComponentWidget( { variable: 'foo' } );\n\n\t\t// override getTemplateData method to delay the rendering\n\t\twidget.getTemplateData = function () {\n\t\t\tconst deferred = $.Deferred();\n\t\t\tsetTimeout( deferred.resolve.bind( deferred, this.state ), 100 );\n\t\t\treturn deferred.promise();\n\t\t};\n\n\t\t// let's wait for in-flight initial render to have completed\n\t\twidget.render().then( function () {\n\t\t\t// render with 'foo' - this should render 'foo' just fine (but it'll\n\t\t\t// take a little since we deferred the template data manipulation)\n\t\t\twidget.setState( { variable: 'foo' } ).then( function ( $element ) {\n\t\t\t\t// since it'll take some time for the initial render to finish\n\t\t\t\tassert.strictEqual( $element.text(), 'foo' );\n\t\t\t} );\n\n\t\t\t// wrap next calls inside setTimout to make sure they're executed\n\t\t\t// while the previous render is ongoing\n\t\t\tsetTimeout( function () {\n\t\t\t\t// set new value\n\t\t\t\twidget.setState( { variable: 'bar' } ).then( function ( $element ) {\n\t\t\t\t\t// since it'll take some time for the first render to finish, this\n\t\t\t\t\t// value will already have been overwritten before we get around\n\t\t\t\t\t// to rendering again, and it'll be combined/optimized away\n\t\t\t\t\tassert.strictEqual( $element.text(), 'baz' );\n\t\t\t\t} );\n\n\t\t\t\t// set another new value - this should be the one that actually gets rendered\n\t\t\t\twidget.setState( { variable: 'baz' } ).then( function ( $element ) {\n\t\t\t\t\t// since it'll take some time for the initial render to finish\n\t\t\t\t\tassert.strictEqual( $element.text(), 'baz' );\n\t\t\t\t\tdone();\n\t\t\t\t} );\n\t\t\t}, 50 );\n\t\t} );\n\t} );\n} );\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/base/DOMLessGroupWidget.test.js","messages":[{"ruleId":"compat/compat","severity":2,"message":"Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11","line":10,"column":37,"nodeType":"MemberExpression","endLine":10,"endColumn":50}],"errorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use strict';\n\nconst sinon = require( 'sinon' ),\n\thooks = require( '../../support/hooks.js' );\nlet DOMLessGroupWidget,\n\tGroupWidget,\n\tItemWidget,\n\tsandbox;\n\nQUnit.module( 'DOMLessGroupWidget', Object.assign( {}, hooks.mediainfo, {\n\tbeforeEach: function () {\n\t\thooks.mediainfo.beforeEach();\n\n\t\tsandbox = sinon.createSandbox();\n\n\t\t// construct an element that mixes in DOMLessGroupWidget, and\n\t\t// an ItemWidget to insert to the group\n\t\tDOMLessGroupWidget = require( '../../../../resources/base/DOMLessGroupWidget.js' );\n\t\tGroupWidget = function ( config ) {\n\t\t\tGroupWidget.parent.call( this, config );\n\t\t\tDOMLessGroupWidget.call( this, config );\n\t\t};\n\t\tOO.inheritClass( GroupWidget, OO.ui.Widget );\n\t\tOO.mixinClass( GroupWidget, DOMLessGroupWidget );\n\n\t\tItemWidget = function ( config ) {\n\t\t\tItemWidget.parent.call( this, config );\n\t\t\tOO.ui.mixin.ItemWidget.call( this, config );\n\t\t};\n\t\tOO.inheritClass( ItemWidget, OO.ui.Widget );\n\t\tOO.mixinClass( ItemWidget, OO.ui.mixin.ItemWidget );\n\t},\n\tafterEach: function () {\n\t\tsandbox.restore();\n\t\thooks.mediainfo.afterEach();\n\t}\n} ), function () {\n\n\tQUnit.test( 'Test item is added to group', function ( assert ) {\n\t\tconst widget = new GroupWidget(),\n\t\t\titem = new ItemWidget();\n\n\t\tassert.strictEqual( widget.getItems().length, 0 );\n\t\twidget.insertItem( item );\n\t\tassert.strictEqual( widget.getItems().length, 1 );\n\t} );\n\n\tQUnit.test( 'Test item DOM is not changed after inserting into group', function ( assert ) {\n\t\tconst widget = new GroupWidget(),\n\t\t\titem = new ItemWidget(),\n\t\t\t$other = $( '<div>' ).append( item.$element );\n\n\t\t// first make sure that the item's node has been attached to\n\t\t// the other container, and that the GroupWidget is empty\n\t\tassert.strictEqual( $other.children().length, 1 );\n\t\tassert.strictEqual( widget.$group.children().length, 0 );\n\n\t\t// then insert the item into a GroupWidget\n\t\twidget.insertItem( item );\n\n\t\t// and now verify that the other container still contains\n\t\t// the node and the GroupWidget's DOM is still empty\n\t\t// (i.e. it didn't steal the item's node from the other)\n\t\tassert.strictEqual( $other.children().length, 1 );\n\t\tassert.strictEqual( widget.$group.children().length, 0 );\n\t} );\n} );\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/filepage/CaptionsPanel.test.js","messages":[{"ruleId":"compat/compat","severity":2,"message":"Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11","line":13,"column":68,"nodeType":"MemberExpression","endLine":13,"endColumn":81}],"errorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use strict';\n\nconst sinon = require( 'sinon' ),\n\tpathToWidget = '../../../../resources/filepage/CaptionsPanel.js',\n\thelpers = require( '../../support/helpers.js' ),\n\thooks = require( '../../support/hooks.js' );\nlet sandbox,\n\tdom,\n\tmediaInfoEntity;\n\nQUnit.module( 'CaptionsPanel', {}, function () {\n\t// CaptionsPanel on page where statements are already present\n\tQUnit.module( 'When pre-existing statements are present on page', Object.assign( {}, hooks.mediainfo, {\n\t\tbeforeEach: function () {\n\t\t\tsandbox = sinon.createSandbox();\n\n\t\t\thooks.mediainfo.beforeEach();\n\t\t\tmediaInfoEntity = helpers.readJSON(\n\t\t\t\t__dirname + '/../../support/fixtures/data/mediaInfoEntity.json'\n\t\t\t);\n\n\t\t\t// pre-construct DOM for jQuery to initialize with\n\t\t\tdom = helpers.generateTemplate( 'captionspanel.mst', 'mediaInfoEntity.json' );\n\t\t\tglobal.window = dom.window;\n\t\t\tglobal.window.scrollTo = function () { /* noop */ };\n\t\t},\n\t\tafterEach: function () {\n\t\t\thooks.mediainfo.afterEach();\n\t\t\tsandbox.restore();\n\t\t}\n\t} ), function () {\n\t\tQUnit.test( 'initialization works without errors', function ( assert ) {\n\t\t\tconst CaptionsPanel = require( pathToWidget ),\n\t\t\t\tconfig = {\n\t\t\t\t\twarnWithinMaxCaptionLength: 20,\n\t\t\t\t\tuserLanguages: [ 'en' ],\n\t\t\t\t\tlanguageFallbackChain: [ 'en' ],\n\t\t\t\t\tmediaInfo: mediaInfoEntity,\n\t\t\t\t\tcanEdit: true\n\t\t\t\t},\n\t\t\t\t// eslint-disable-next-line no-unused-vars\n\t\t\t\tcp = new CaptionsPanel( config );\n\n\t\t\tassert.ok( true );\n\t\t} );\n\n\t\tQUnit.test( 'user languages are added to DOM', function ( assert ) {\n\t\t\tconst CaptionsPanel = require( pathToWidget ),\n\t\t\t\tuserLanguages = [ 'en', 'ga', 'de' ],\n\t\t\t\tconfig = {\n\t\t\t\t\twarnWithinMaxCaptionLength: 20,\n\t\t\t\t\tuserLanguages: userLanguages,\n\t\t\t\t\tlanguageFallbackChain: [ 'en' ],\n\t\t\t\t\tmediaInfo: mediaInfoEntity,\n\t\t\t\t\tcanEdit: true\n\t\t\t\t},\n\t\t\t\tcp = new CaptionsPanel( config ),\n\t\t\t\tdone = assert.async();\n\n\t\t\t// There should be a new caption row for every user language that doesn't already\n\t\t\t// exist in the caption data\n\t\t\tconst captionLanguages = Object.keys( mediaInfoEntity.labels );\n\t\t\tuserLanguages.forEach( function ( langCode ) {\n\t\t\t\tif ( captionLanguages.indexOf( langCode ) === -1 ) {\n\t\t\t\t\tcaptionLanguages.push( langCode );\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\tcp.renderPromise.then( function () {\n\t\t\t\tassert.strictEqual(\n\t\t\t\t\tcp.$element.find( '.wbmi-entityview-caption' ).length,\n\t\t\t\t\tcaptionLanguages.length\n\t\t\t\t);\n\t\t\t\tdone();\n\t\t\t} );\n\t\t} );\n\t} );\n} );\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/filepage/LicenseDialogWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/filepage/ProtectionMsgWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/filepage/StatementPanel.test.js","messages":[{"ruleId":"compat/compat","severity":2,"message":"Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11","line":14,"column":71,"nodeType":"MemberExpression","endLine":14,"endColumn":84}],"errorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use strict';\n\n/* eslint-disable no-jquery/no-global-selector */\n\nconst sinon = require( 'sinon' ),\n\tpathToWidget = '../../../../resources/filepage/StatementPanel.js',\n\thelpers = require( '../../support/helpers.js' ),\n\thooks = require( '../../support/hooks.js' );\nlet sandbox,\n\tdom;\n\nQUnit.module( 'StatementPanel', {}, function () {\n\t// Scenario 1. StatementsPanel on page where no statements are present\n\tQUnit.module( 'When no pre-existing statements are present on page', Object.assign( {}, hooks.mediainfo, {\n\t\tbeforeEach: function () {\n\t\t\tsandbox = sinon.createSandbox();\n\n\t\t\t// pre-construct DOM for jQuery to initialize with\n\t\t\tdom = helpers.generateTemplate( 'statementpanel.mst', 'paneldata-empty.json' );\n\t\t\tglobal.window = dom.window;\n\n\t\t\thooks.mediainfo.beforeEach();\n\t\t},\n\t\tafterEach: function () {\n\t\t\thooks.mediainfo.afterEach();\n\t\t\tsandbox.restore();\n\t\t}\n\t} ), function () {\n\t\tQUnit.test( 'constructor', function ( assert ) {\n\t\t\tconst StatementPanel = require( pathToWidget ),\n\t\t\t\tconfig = {\n\t\t\t\t\t$element: $( '.wbmi-entityview-statementsGroup' ),\n\t\t\t\t\tpropertyId: 'P1',\n\t\t\t\t\tpropertyType: 'wikibase-item',\n\t\t\t\t\tentityId: 'M1',\n\t\t\t\t\tproperties: { P1: 'wikibase-item' }\n\t\t\t\t};\n\n\t\t\t// eslint-disable-next-line no-new\n\t\t\tnew StatementPanel( config );\n\n\t\t\tassert.ok( true );\n\t\t} );\n\n\t\tQUnit.test( 'isEditable() is false by default', function ( assert ) {\n\t\t\tconst StatementPanel = require( pathToWidget ),\n\t\t\t\tconfig = {\n\t\t\t\t\t$element: $( '.wbmi-entityview-statementsGroup' ),\n\t\t\t\t\tpropertyId: 'P1',\n\t\t\t\t\tpropertyType: 'wikibase-item',\n\t\t\t\t\tentityId: 'M1',\n\t\t\t\t\tproperties: { P1: 'wikibase-item' }\n\t\t\t\t},\n\t\t\t\tsp = new StatementPanel( config );\n\n\t\t\tassert.strictEqual( sp.isEditable(), false );\n\t\t} );\n\n\t\t// Scenario 1.1: Anon user\n\t\tQUnit.module( 'User is not logged in and has not accepted license', {\n\t\t\tbeforeEach: function () {\n\t\t\t\tglobal.mw.user = helpers.createMediaWikiUser();\n\t\t\t}\n\t\t}, function () {\n\t\t\t// Async test\n\t\t\tQUnit.test( 'LicenseDialogWidget is displayed when user attempts to edit', function ( assert ) {\n\t\t\t\tconst StatementPanel = require( pathToWidget ),\n\t\t\t\t\tconfig = {\n\t\t\t\t\t\t$element: $( '.wbmi-entityview-statementsGroup' ),\n\t\t\t\t\t\tpropertyId: 'P1',\n\t\t\t\t\t\tpropertyType: 'wikibase-item',\n\t\t\t\t\t\tentityId: 'M1',\n\t\t\t\t\t\tproperties: { P1: 'wikibase-item' }\n\t\t\t\t\t},\n\t\t\t\t\tsp = new StatementPanel( config ),\n\t\t\t\t\tdone = assert.async();\n\n\t\t\t\tconst spy = sinon.spy( sp.licenseDialogWidget, 'openDialog' );\n\t\t\t\tsp.makeEditable();\n\n\t\t\t\tsetTimeout( function () {\n\t\t\t\t\tassert.strictEqual( spy.called, true );\n\t\t\t\t\tdone();\n\t\t\t\t}, 100 );\n\t\t\t} );\n\t\t} );\n\t} );\n} );\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/mediainfo.template.mustache+dom.test.js","messages":[{"ruleId":"compat/compat","severity":2,"message":"Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11","line":7,"column":50,"nodeType":"MemberExpression","endLine":7,"endColumn":63}],"errorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use strict';\n\nconst sinon = require( 'sinon' ),\n\thooks = require( '../support/hooks.js' );\nlet sandbox;\n\nQUnit.module( 'mediainfo.template.mustache+dom', Object.assign( {}, hooks.mediawiki, {\n\tbeforeEach: function () {\n\t\thooks.mediawiki.beforeEach();\n\n\t\tsandbox = sinon.createSandbox();\n\t\tsandbox.stub( mw.templates, 'get' ).returns( {\n\t\t\t'test.mustache+dom': '<div>{{{foo}}}</div>'\n\t\t} );\n\t},\n\tafterEach: function () {\n\t\tsandbox.restore();\n\t\thooks.mediainfo.afterEach();\n\t}\n} ), function () {\n\tQUnit.test( 'Render mustache templates', function ( assert ) {\n\t\tconst template = mw.template.get( 'stub', 'test.mustache+dom' );\n\n\t\tconst data = {\n\t\t\tfoo: 'Hello world'\n\t\t};\n\n\t\tconst html = template.render( data ).html();\n\n\t\tassert.strictEqual( html, 'Hello world', 'Rendered mustache template' );\n\t} );\n\n\tQUnit.module( 'Mustache templates with HTMLElement', {}, function () {\n\t\tQUnit.test( 'Nodes are parsed into template', function ( assert ) {\n\t\t\tconst template = mw.template.get( 'stub', 'test.mustache+dom' ),\n\t\t\t\tnode = document.createElement( 'div' );\n\n\t\t\tnode.id = 'test';\n\t\t\tnode.innerHTML = 'Hello world';\n\n\t\t\tconst $result = template.render( { foo: node } );\n\n\t\t\tassert.strictEqual( $result.find( '#test' ).length, 1, 'Node is rendered' );\n\t\t\tassert.strictEqual( $result.find( '#test' ).text(), 'Hello world', 'Node contains content' );\n\t\t} );\n\n\t\tQUnit.test( 'Events triggered from template-based HTML propagate to original element handlers', function ( assert ) {\n\t\t\tconst template = mw.template.get( 'stub', 'test.mustache+dom' ),\n\t\t\t\tonClick = sinon.stub(),\n\t\t\t\tnode = document.createElement( 'div' );\n\n\t\t\tnode.id = 'test';\n\t\t\tnode.innerHTML = 'Hello world';\n\t\t\tnode.onclick = onClick;\n\n\t\t\tconst $result = template.render( { foo: node } );\n\n\t\t\tassert.strictEqual( onClick.callCount, 0, 'Event not yet triggered' );\n\t\t\t$result.find( '#test' ).trigger( 'click' );\n\t\t\tassert.strictEqual( onClick.callCount, 1, 'Event triggered' );\n\t\t} );\n\n\t\tQUnit.test( 'Changes to node later on propagate into DOM rendered by template', function ( assert ) {\n\t\t\tconst template = mw.template.get( 'stub', 'test.mustache+dom' ),\n\t\t\t\tnode = document.createElement( 'div' );\n\n\t\t\tnode.id = 'test';\n\t\t\tnode.innerHTML = 'Hello world';\n\n\t\t\tconst $result = template.render( { foo: node } );\n\n\t\t\tassert.strictEqual( $result.find( '#test-updated' ).length, 0, 'Element in DOM not yet altered' );\n\t\t\tnode.id = 'test-updated';\n\t\t\tassert.strictEqual( $result.find( '#test-updated' ).length, 1, 'Element in DOM altered' );\n\t\t} );\n\t} );\n\n\tQUnit.module( 'Mustache templates with jQuery nodes', {}, function () {\n\t\tQUnit.test( 'Nodes are parsed into template', function ( assert ) {\n\t\t\tconst template = mw.template.get( 'stub', 'test.mustache+dom' ),\n\t\t\t\t$node = $( '<div>' )\n\t\t\t\t\t.attr( 'id', 'test' )\n\t\t\t\t\t.text( 'Hello world' );\n\n\t\t\tconst $result = template.render( { foo: $node } );\n\n\t\t\tassert.strictEqual( $result.find( '#test' ).length, 1, 'Node is rendered' );\n\t\t\tassert.strictEqual( $result.find( '#test' ).text(), 'Hello world', 'Node contains content' );\n\t\t} );\n\n\t\tQUnit.test( 'Events triggered from template-based HTML propagate to original element handlers', function ( assert ) {\n\t\t\tconst template = mw.template.get( 'stub', 'test.mustache+dom' ),\n\t\t\t\tonClick = sinon.stub(),\n\t\t\t\t$node = $( '<div>' )\n\t\t\t\t\t.attr( 'id', 'test' )\n\t\t\t\t\t.text( 'Hello world' )\n\t\t\t\t\t.on( 'click', onClick );\n\n\t\t\tconst $result = template.render( { foo: $node } );\n\n\t\t\tassert.strictEqual( onClick.callCount, 0, 'Event not yet triggered' );\n\t\t\t$result.find( '#test' ).trigger( 'click' );\n\t\t\tassert.strictEqual( onClick.callCount, 1, 'Event triggered' );\n\t\t} );\n\n\t\tQUnit.test( 'Changes to node later on propagate into DOM rendered by template', function ( assert ) {\n\t\t\tconst template = mw.template.get( 'stub', 'test.mustache+dom' ),\n\t\t\t\t$node = $( '<div>' )\n\t\t\t\t\t.attr( 'id', 'test' )\n\t\t\t\t\t.text( 'Hello world' );\n\n\t\t\tconst $result = template.render( { foo: $node } );\n\n\t\t\tassert.strictEqual( $result.find( '#test-updated' ).length, 0, 'Element in DOM not yet altered' );\n\t\t\t$node.attr( 'id', 'test-updated' );\n\t\t\tassert.strictEqual( $result.find( '#test-updated' ).length, 1, 'Element in DOM altered' );\n\t\t} );\n\t} );\n\n\tQUnit.module( 'Mustache templates with OOUI widgets', {}, function () {\n\t\tQUnit.test( 'Nodes are parsed into template', function ( assert ) {\n\t\t\tconst template = mw.template.get( 'stub', 'test.mustache+dom' ),\n\t\t\t\twidget = new OO.ui.Widget( {\n\t\t\t\t\tid: 'test',\n\t\t\t\t\ttext: 'Hello world'\n\t\t\t\t} );\n\n\t\t\tconst $result = template.render( { foo: widget } );\n\n\t\t\tassert.strictEqual( $result.find( '#test' ).length, 1, 'Node is rendered' );\n\t\t\tassert.strictEqual( $result.find( '#test' ).text(), 'Hello world', 'Node contains content' );\n\t\t} );\n\n\t\tQUnit.test( 'Events triggered from template-based HTML propagate to original element handlers', function ( assert ) {\n\t\t\tconst template = mw.template.get( 'stub', 'test.mustache+dom' ),\n\t\t\t\tonClick = sinon.stub(),\n\t\t\t\twidget = new OO.ui.Widget( {\n\t\t\t\t\tid: 'test',\n\t\t\t\t\ttext: 'Hello world'\n\t\t\t\t} );\n\n\t\t\t// wire up widget to emit an OOUI event by something triggered in DOM\n\t\t\twidget.$element.on( 'click', widget.emit.bind( widget, 'click' ) );\n\t\t\twidget.on( 'click', onClick );\n\n\t\t\tconst $result = template.render( { foo: widget } );\n\n\t\t\tassert.strictEqual( onClick.callCount, 0, 'Event not yet triggered' );\n\t\t\t$result.find( '#test' ).trigger( 'click' );\n\t\t\tassert.strictEqual( onClick.callCount, 1, 'Event triggered' );\n\t\t} );\n\n\t\tQUnit.test( 'Changes to node later on propagate into DOM rendered by template', function ( assert ) {\n\t\t\tconst template = mw.template.get( 'stub', 'test.mustache+dom' ),\n\t\t\t\twidget = new OO.ui.Widget( {\n\t\t\t\t\tid: 'test',\n\t\t\t\t\ttext: 'Hello world'\n\t\t\t\t} );\n\n\t\t\tconst $result = template.render( { foo: widget } );\n\n\t\t\tassert.strictEqual( $result.find( '#test-updated' ).length, 0, 'Element in DOM not yet altered' );\n\t\t\twidget.setElementId( 'test-updated' );\n\t\t\tassert.strictEqual( $result.find( '#test-updated' ).length, 1, 'Element in DOM altered' );\n\t\t} );\n\t} );\n} );\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/statements/AddPropertyWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/statements/ItemWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/statements/LinkNoticeWidget.test.js","messages":[{"ruleId":"compat/compat","severity":2,"message":"Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11","line":9,"column":35,"nodeType":"MemberExpression","endLine":9,"endColumn":48}],"errorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use strict';\n\nconst sinon = require( 'sinon' ),\n\tpathToWidget = '../../../../resources/statements/LinkNoticeWidget.js',\n\thelpers = require( '../../support/helpers.js' ),\n\thooks = require( '../../support/hooks.js' ),\n\tprefKey = 'wbmi-wikidata-link-notice-dismissed';\n\nQUnit.module( 'LinkNoticeWidget', Object.assign( {}, hooks.mediainfo, {\n\tbeforeEach: function () {\n\t\thooks.mediainfo.beforeEach();\n\n\t\t// pretend config conditions for showing notice are met\n\t\tglobal.mw.message = sinon.stub().returns( {\n\t\t\texists: sinon.stub().withArgs( 'wikibasemediainfo-statements-link-notice-text' ).returns( true ),\n\t\t\ttext: sinon.stub().withArgs( 'wikibasemediainfo-statements-link-notice-text' ).returns( 'Stub text' )\n\t\t} );\n\t}\n} ), function () {\n\tQUnit.test( 'constructor', function ( assert ) {\n\t\tconst LinkNoticeWidget = require( pathToWidget );\n\n\t\tglobal.mw.user = helpers.createMediaWikiUser( false );\n\n\t\t/* eslint-disable-next-line no-new */\n\t\tnew LinkNoticeWidget();\n\t\tassert.ok( true );\n\t} );\n\n\tQUnit.module( 'User is not logged in.', {\n\t\tbeforeEach: function () {\n\t\t\tglobal.mw.user = helpers.createMediaWikiUser( false );\n\n\t\t\tglobal.mw.storage.get.returns( 0 );\n\t\t}\n\t}, function () {\n\t\tQUnit.test( 'Widget should be visible if not previously dismissed', function ( assert ) {\n\t\t\tconst LinkNoticeWidget = require( pathToWidget ),\n\t\t\t\twidget = new LinkNoticeWidget();\n\t\t\tassert.strictEqual( widget.isDismissed(), false );\n\t\t} );\n\n\t\tQUnit.test( 'Widget should not be visible if previously dismissed', function ( assert ) {\n\t\t\tconst LinkNoticeWidget = require( pathToWidget );\n\n\t\t\t// Fake out previous dismissal in localstorage\n\t\t\tglobal.mw.storage.get.returns( 1 );\n\n\t\t\tconst widget = new LinkNoticeWidget();\n\n\t\t\tassert.strictEqual( widget.isDismissed(), true );\n\t\t} );\n\n\t\tQUnit.test( 'dismiss method should store data in local storage for anon users', function ( assert ) {\n\t\t\tconst done = assert.async(),\n\t\t\t\tLinkNoticeWidget = require( pathToWidget ),\n\t\t\t\twidget = new LinkNoticeWidget();\n\n\t\t\twidget.dismiss().then( function () {\n\t\t\t\tassert.strictEqual( global.mw.storage.set.calledWith( prefKey, 1 ), true );\n\t\t\t\tdone();\n\t\t\t} );\n\t\t} );\n\n\t\tQUnit.test( 'dismiss method should dismiss the widget', function ( assert ) {\n\t\t\tconst done = assert.async(),\n\t\t\t\tLinkNoticeWidget = require( pathToWidget ),\n\t\t\t\twidget = new LinkNoticeWidget();\n\n\t\t\t// wait for initial render to complete\n\t\t\twidget.render().then( function ( $element ) {\n\t\t\t\tassert.strictEqual( $element.find( '.wbmi-link-notice' ).length, 1 );\n\t\t\t\twidget.dismiss().then( function ( $innerElement ) {\n\t\t\t\t\tassert.strictEqual( $innerElement.find( '.wbmi-link-notice' ).length, 0 );\n\t\t\t\t\tdone();\n\t\t\t\t} );\n\t\t\t} );\n\t\t} );\n\t} );\n\n\tQUnit.module( 'User is logged-in', {\n\t\tbeforeEach: function () {\n\t\t\tglobal.mw.user = helpers.createMediaWikiUser( true );\n\t\t\tglobal.mw.Api = function () {};\n\t\t\tglobal.mw.Api.prototype = {\n\t\t\t\tsaveOption: sinon.stub()\n\t\t\t};\n\t\t}\n\t}, function () {\n\t\tQUnit.test( 'Widget should be visible if not previously dismissed', function ( assert ) {\n\t\t\tconst LinkNoticeWidget = require( pathToWidget ),\n\t\t\t\twidget = new LinkNoticeWidget();\n\t\t\tassert.strictEqual( widget.isDismissed(), false );\n\t\t} );\n\n\t\tQUnit.test( 'Widget should not be visible if previously dismissed', function ( assert ) {\n\t\t\tconst LinkNoticeWidget = require( pathToWidget );\n\n\t\t\t// Fake out previous dismissal in user prefs\n\t\t\tglobal.mw.user.options.get.returns( 1 );\n\n\t\t\tconst widget = new LinkNoticeWidget();\n\t\t\tassert.strictEqual( widget.isDismissed(), true );\n\t\t} );\n\n\t\tQUnit.test( 'dismiss method should store data in user preferences for logged in users', function ( assert ) {\n\t\t\tconst done = assert.async(),\n\t\t\t\tLinkNoticeWidget = require( pathToWidget ),\n\t\t\t\twidget = new LinkNoticeWidget();\n\n\t\t\twidget.dismiss().then( function () {\n\t\t\t\tassert.strictEqual( global.mw.user.options.set.calledWith( prefKey, 1 ), true );\n\t\t\t\tassert.strictEqual( global.mw.Api.prototype.saveOption.calledWith( prefKey, 1 ), true );\n\t\t\t\tdone();\n\t\t\t} );\n\t\t} );\n\t} );\n} );\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/statements/QualifierWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/statements/StatementWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/statements/inputs/EntityInputWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/statements/inputs/GlobeCoordinateInputWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/statements/inputs/MonolingualTextInputWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/statements/inputs/MultiTypeInputWrapperWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/statements/inputs/QuantityInputWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/statements/inputs/StringInputWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/mediainfo/statements/inputs/TimeInputWidget.test.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/support/fixtures/data/coordinateData.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/support/fixtures/data/mediaInfoEntity.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/support/fixtures/data/paneldata-empty.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/support/fixtures/data/qualifierMenuOptionData.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/support/fixtures/data/wbDataTypes.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/support/fixtures/data/wbmiPropertyTypes.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/support/helpers.js","messages":[{"ruleId":"compat/compat","severity":2,"message":"Map is not supported in Safari 5.1, iOS Safari 6.0-6.1","line":103,"column":14,"nodeType":"NewExpression","endLine":103,"endColumn":23}],"errorCount":1,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use strict';\n\nconst sinon = require( 'sinon' ),\n\tjsdom = require( 'jsdom' ),\n\tfs = require( 'fs' ),\n\tpath = require( 'path' ),\n\tMustache = require( 'mustache' ),\n\tmockery = require( 'mockery' ),\n\tmockCache = {};\n\n/**\n * Allows requiring a module more than once.\n * Useful for e.g. wikibase files, which aren't really modules,\n * but code that is executed immediately, which we'll want to\n * run before every test.\n *\n * @param {string} module\n * @return {*}\n */\nfunction requireAgain( module ) {\n\ttry {\n\t\tdelete require.cache[ require.resolve( module ) ];\n\t} catch ( e ) {\n\t\t// couldn't resolve module, so there'll be no cache for sure\n\t}\n\treturn require( module );\n}\nmodule.exports.requireAgain = requireAgain;\n\n/**\n * Builds a template for use in testing when given a Mustache template and JSON\n * data to populate it. Paths should be relative to the \"support\" directory.\n *\n * @param {string} pathToTemplate\n * @param {string} pathToData\n * @return {jsdom.JSDOM} JSDOM object\n */\nmodule.exports.generateTemplate = function ( pathToTemplate, pathToData ) {\n\tconst template = fs.readFileSync( path.join( __dirname, 'templates', pathToTemplate ), 'utf8' ),\n\t\tdata = require( './fixtures/data/' + pathToData ),\n\t\thtml = Mustache.render( template, data ),\n\t\tdom = new jsdom.JSDOM( html );\n\n\treturn dom;\n};\n\n/**\n * Returns the contents of a json file as a js object\n *\n * @param {string} pathToFile\n * @return {Object}\n */\nmodule.exports.readJSON = function ( pathToFile ) {\n\treturn JSON.parse( fs.readFileSync( pathToFile, 'utf8' ) );\n};\n\n/**\n * Stubs out a basic \"mw\" object for use in testing. Only stubs out\n * properties/methods that need to be called in the test suite; expect more\n * additions over time as the suite grows.\n *\n * @return {Object} mw\n */\nmodule.exports.createMediaWikiEnv = function () {\n\treturn {\n\t\tmediaInfo: {\n\t\t\tstructuredData: {}\n\t\t},\n\n\t\tconfig: {\n\t\t\tget: sinon.stub(),\n\t\t\tset: sinon.stub()\n\t\t},\n\n\t\tnotify: sinon.stub(),\n\n\t\tstorage: {\n\t\t\tget: sinon.stub(),\n\t\t\tset: sinon.stub()\n\t\t},\n\n\t\tcookie: {\n\t\t\tget: sinon.stub(),\n\t\t\tset: sinon.stub()\n\t\t},\n\n\t\tmessage: sinon.stub().returns( {\n\t\t\texists: sinon.stub(),\n\t\t\ttext: sinon.stub(),\n\t\t\tparse: sinon.stub()\n\t\t} ),\n\n\t\ttemplate: {\n\t\t\tget: sinon.stub().returns( {\n\t\t\t\trender: sinon.stub()\n\t\t\t} )\n\t\t},\n\n\t\tloader: {\n\t\t\tusing: sinon.stub().resolves( sinon.stub() )\n\t\t},\n\n\t\ttemplates: new Map(),\n\n\t\tTitle: sinon.stub().returns( {\n\t\t\tgetNameText: sinon.stub(),\n\t\t\tgetNamespaceId: sinon.stub()\n\t\t} ),\n\n\t\thtml: {\n\t\t\tescape: sinon.stub()\n\t\t}\n\t};\n};\n\n/**\n * Stubs out and/or loads a basic \"globeCoordinate\" object for use in testing.\n *\n * @return {Object}\n */\nmodule.exports.createGlobeCoordinateEnv = function () {\n\tconst oldglobeCoordinate = global.globeCoordinate,\n\t\toldJQuery = global.jQuery,\n\t\told$ = global.$;\n\n\t// `require` caches the exports and reuses them the next require\n\t// the files required below have no exports, though - they just\n\t// execute and are assigned as properties of an object\n\t// `requireAgain` would make sure they keep doing that over and\n\t// over, but then they'll end up creating the same functions/objects\n\t// more than once, but different instances...\n\t// other modules, with actual exports, that use these functions\n\t// might encounter side-effects though, because the instances of\n\t// those objects are different when loaded at different times,\n\t// so to be safe, we'll try to emulate regular `require` behavior\n\t// by running these files once, grabbing the result, caching it,\n\t// and re-using the result from cache\n\tif ( mockCache.globeCoordinate ) {\n\t\treturn mockCache.globeCoordinate;\n\t}\n\n\t// wikibase-data-values needs jquery...\n\tglobal.jQuery = global.$ = requireAgain( 'jquery' );\n\n\tglobal.globeCoordinate = requireAgain( 'wikibase-data-values/lib/globeCoordinate/globeCoordinate.js' ).globeCoordinate;\n\trequireAgain( 'wikibase-data-values/lib/globeCoordinate/globeCoordinate.GlobeCoordinate.js' );\n\n\tmockCache.globeCoordinate = global.globeCoordinate;\n\tglobal.globeCoordinate = oldglobeCoordinate;\n\n\t// restore global scope before returning\n\tglobal.jQuery = oldJQuery;\n\tglobal.$ = old$;\n\n\treturn mockCache.globeCoordinate;\n};\n\n/**\n * Stubs out and/or loads a basic \"dataValues\" object for use in testing.\n *\n * @return {Object}\n */\nmodule.exports.createDataValuesEnv = function () {\n\tconst oldDataValues = global.dataValues,\n\t\toldUtil = global.util,\n\t\toldJQuery = global.jQuery,\n\t\told$ = global.$;\n\n\t// `require` caches the exports and reuses them the next require\n\t// the files required below have no exports, though - they just\n\t// execute and are assigned as properties of an object\n\t// `requireAgain` would make sure they keep doing that over and\n\t// over, but then they'll end up creating the same functions/objects\n\t// more than once, but different instances...\n\t// other modules, with actual exports, that use these functions\n\t// might encounter side-effects though, because the instances of\n\t// those objects are different when loaded at different times,\n\t// so to be safe, we'll try to emulate regular `require` behavior\n\t// by running these files once, grabbing the result, caching it,\n\t// and re-using the result from cache\n\tif ( mockCache.dataValues ) {\n\t\treturn mockCache.dataValues;\n\t}\n\n\t// wikibase-data-values needs jquery...\n\tglobal.jQuery = global.$ = requireAgain( 'jquery' );\n\n\tglobal.dataValues = requireAgain( 'wikibase-data-values/src/dataValues.js' ).dataValues;\n\tglobal.util = {};\n\n\trequireAgain( 'wikibase-data-values/lib/util/util.inherit.js' );\n\trequireAgain( 'wikibase-data-values/src/DataValue.js' );\n\trequireAgain( 'wikibase-data-values/src/values/StringValue.js' );\n\trequireAgain( 'wikibase-data-values/src/values/DecimalValue.js' );\n\trequireAgain( 'wikibase-data-values/src/values/MonolingualTextValue.js' );\n\trequireAgain( 'wikibase-data-values/src/values/QuantityValue.js' );\n\trequireAgain( 'wikibase-data-values/src/values/TimeValue.js' );\n\trequireAgain( 'wikibase-data-values/src/values/GlobeCoordinateValue.js' );\n\trequireAgain( 'wikibase-data-values/src/values/UnknownValue.js' );\n\n\tmockCache.dataValues = global.dataValues;\n\n\t// restore global scope before returning\n\tglobal.dataValues = oldDataValues;\n\tglobal.util = oldUtil;\n\tglobal.jQuery = oldJQuery;\n\tglobal.$ = old$;\n\n\treturn mockCache.dataValues;\n};\n\n/**\n * Stubs out and/or loads a basic \"wikibase\" object for use in testing.\n *\n * @return {Object}\n */\nmodule.exports.createWikibaseEnv = function () {\n\treturn {\n\t\tapi: {\n\t\t\tgetLocationAgnosticMwApi: sinon.stub().returns( {\n\t\t\t\tget: sinon.stub().returns( $.Deferred().resolve( {} ).promise( { abort: function () {} } ) ),\n\t\t\t\tpost: sinon.stub().returns( $.Deferred().resolve( {} ).promise( { abort: function () {} } ) )\n\t\t\t} )\n\t\t},\n\t\tutilities: {\n\t\t\tClaimGuidGenerator: sinon.stub().returns( { newGuid: function () { return Math.random().toString( 36 ).slice( 2 ); } } )\n\t\t}\n\t};\n};\n\n/**\n * Loads a \"wikibase.datamodel\" object for use in testing.\n */\nmodule.exports.registerWbDataModel = function () {\n\tglobal.dataValues = this.createDataValuesEnv();\n\tglobal.util = {};\n\n\trequireAgain( 'wikibase-data-values/lib/util/util.inherit.js' );\n\n\tmockery.registerSubstitute( 'wikibase.datamodel', 'wikibase-data-model/src/index.js' );\n};\n\nmodule.exports.registerWbSerialization = function () {\n\tglobal.util = {};\n\n\trequireAgain( 'wikibase-data-values/lib/util/util.inherit.js' );\n\n\tmockery.registerSubstitute( 'wikibase.serialization', 'wikibase-serialization/src/index.js' );\n};\n\n/**\n * Stubs out a basic stand-in for the mw.user object.\n *\n * @param {boolean} loggedIn Whether to simulate a logged-in user\n * @return {Object} user\n */\nmodule.exports.createMediaWikiUser = function ( loggedIn ) {\n\tconst user = {\n\t\tisAnon: sinon.stub(),\n\t\toptions: {\n\t\t\tget: sinon.stub(),\n\t\t\tset: sinon.stub()\n\t\t}\n\t};\n\n\tif ( loggedIn ) {\n\t\tuser.isAnon.returns( false );\n\t} else {\n\t\tuser.isAnon.returns( true );\n\t}\n\n\treturn user;\n};\n\nmodule.exports.requireULS = function () {\n\trequireAgain( 'jquery.uls/src/jquery.uls.data.js' );\n\trequireAgain( 'jquery.uls/src/jquery.uls.data.utils.js' );\n\trequireAgain( 'jquery.uls/src/jquery.uls.core.js' );\n\trequireAgain( 'jquery.uls/src/jquery.uls.lcd.js' );\n\trequireAgain( 'jquery.uls/src/jquery.uls.languagefilter.js' );\n};\n\nmodule.exports.registerModules = function () {\n\tconst extensionJson = this.readJSON( path.join( __dirname, '..', '..', '..', 'extension.json' ) ),\n\t\tmodules = extensionJson.ResourceModules;\n\n\tObject.keys( modules ).forEach( function ( moduleName ) {\n\t\tconst packageFiles = modules[ moduleName ].packageFiles;\n\t\tif ( !packageFiles ) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tmockery.registerMock( moduleName, require( path.join( __dirname, '..', '..', '..', packageFiles[ 0 ] ) ) );\n\t\t} catch ( e ) {\n\t\t\t// failed to include, but that could be ok, it might just expect immediate\n\t\t\t// execution in the browser - we'll have to deal with this module not\n\t\t\t// being available for JS tests\n\t\t}\n\t} );\n};\n\nmodule.exports.registerTemplates = function () {\n\tconst extensionJson = this.readJSON( path.join( __dirname, '..', '..', '..', 'extension.json' ) ),\n\t\tmodules = extensionJson.ResourceModules;\n\n\tObject.keys( modules ).forEach( function ( moduleName ) {\n\t\tconst templates = modules[ moduleName ].templates;\n\t\tif ( !templates ) {\n\t\t\treturn;\n\t\t}\n\n\t\ttemplates.forEach( function ( templateName ) {\n\t\t\tconst template = fs.readFileSync( path.join( __dirname, '..', '..', '..', templateName ), 'utf8' );\n\t\t\tglobal.mw.template.add( moduleName, templateName, template );\n\t\t} );\n\t} );\n};\n\nmodule.exports.deregisterModules = function () {\n\tconst extensionJson = this.readJSON( path.join( __dirname, '..', '..', '..', 'extension.json' ) ),\n\t\tmodules = extensionJson.ResourceModules;\n\n\tObject.keys( modules ).forEach( function ( moduleName ) {\n\t\tmockery.deregisterMock( moduleName );\n\t} );\n};\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/node-qunit/support/hooks.js","messages":[{"ruleId":"compat/compat","severity":2,"message":"Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11","line":32,"column":23,"nodeType":"MemberExpression","endLine":32,"endColumn":36},{"ruleId":"compat/compat","severity":2,"message":"Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11","line":52,"column":28,"nodeType":"MemberExpression","endLine":52,"endColumn":41},{"ruleId":"compat/compat","severity":2,"message":"Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11","line":77,"column":27,"nodeType":"MemberExpression","endLine":77,"endColumn":40},{"ruleId":"compat/compat","severity":2,"message":"Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11","line":105,"column":28,"nodeType":"MemberExpression","endLine":105,"endColumn":41},{"ruleId":"compat/compat","severity":2,"message":"Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11","line":129,"column":31,"nodeType":"MemberExpression","endLine":129,"endColumn":44}],"errorCount":5,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use strict';\n\nconst sinon = require( 'sinon' ),\n\tjsdom = require( 'jsdom' ),\n\thelpers = require( './helpers.js' ),\n\tmockery = require( 'mockery' ),\n\twbDataTypes = require( './fixtures/data/wbDataTypes.json' ),\n\twbmiPropertyTypes = require( './fixtures/data/wbmiPropertyTypes.json' ),\n\tsandboxes = {};\nlet dom;\n\nmodule.exports.jquery = {\n\tbeforeEach: function () {\n\t\tsandboxes.jquery = sinon.createSandbox();\n\n\t\t// construct DOM is none exists yet\n\t\tif ( !global.window ) {\n\t\t\tdom = new jsdom.JSDOM( '<!doctype html><html lang=\"en\"><body></body></html>' );\n\t\t\tglobal.window = dom.window;\n\t\t}\n\t\tglobal.window.scrollTo = function () { /* noop */ };\n\n\t\tglobal.document = global.window.document;\n\t\tglobal.jQuery = global.$ = global.window.jQuery = global.window.$ = require( 'jquery' );\n\t},\n\tafterEach: function () {\n\t\tdelete require.cache[ require.resolve( 'jquery' ) ];\n\t\tsandboxes.jquery.restore();\n\t}\n};\n\nmodule.exports.ooui = Object.assign( {}, module.exports.jquery, {\n\tbeforeEach: function () {\n\t\t// jQuery is a requirement for OOUI\n\t\tmodule.exports.jquery.beforeEach();\n\n\t\tsandboxes.ooui = sinon.createSandbox();\n\n\t\tglobal.OO = require( 'oojs' );\n\t\trequire( 'oojs-ui' );\n\t\trequire( '../../../node_modules/oojs-ui/dist/oojs-ui-wikimediaui.js' );\n\t},\n\tafterEach: function () {\n\t\tdelete require.cache[ require.resolve( 'oojs-ui/dist/oojs-ui-wikimediaui.js' ) ];\n\t\tdelete require.cache[ require.resolve( 'oojs-ui' ) ];\n\t\tdelete require.cache[ require.resolve( 'oojs' ) ];\n\t\tsandboxes.ooui.restore();\n\t\tmodule.exports.jquery.afterEach();\n\t}\n} );\n\nmodule.exports.mediawiki = Object.assign( {}, module.exports.jquery, {\n\tbeforeEach: function () {\n\t\t// jQuery is a requirement for MediaWiki\n\t\tmodule.exports.jquery.beforeEach();\n\n\t\tsandboxes.mediawiki = sinon.createSandbox();\n\n\t\tglobal.mw = helpers.createMediaWikiEnv();\n\n\t\t// expand jQuery with MediaWiki-specific plugin\n\t\tglobal.$.fn.msg = sinon.stub(); // stub for the jQuery msg plugin\n\n\t\t// Setup Mustache & templating\n\t\t// @todo refactor createMediaWikiEnv, and load *this* inside there...\n\t\tglobal.Mustache = require( 'mustache' );\n\t\thelpers.requireAgain( 'mediawiki/resources/src/mediawiki.template.js' );\n\t\thelpers.requireAgain( 'mediawiki/resources/src/mediawiki.template.mustache.js' );\n\t\thelpers.requireAgain( '../../../resources/mediawiki.template.mustache+dom.js' );\n\t},\n\tafterEach: function () {\n\t\tsandboxes.mediawiki.restore();\n\t\tmodule.exports.jquery.afterEach();\n\t}\n} );\n\nmodule.exports.wikibase = Object.assign( {}, module.exports.mediawiki, {\n\tbeforeEach: function () {\n\t\t// MediaWiki is a requirement for Wikibase\n\t\tmodule.exports.mediawiki.beforeEach();\n\n\t\tmockery.enable( {\n\t\t\twarnOnReplace: false,\n\t\t\twarnOnUnregistered: false\n\t\t} );\n\n\t\tsandboxes.wikibase = sinon.createSandbox();\n\n\t\tglobal.globeCoordinate = helpers.createGlobeCoordinateEnv();\n\t\tglobal.dataValues = helpers.createDataValuesEnv();\n\t\tglobal.wikibase = helpers.createWikibaseEnv();\n\t\thelpers.registerWbDataModel();\n\t\tglobal.mw.config.get.withArgs( 'wbDataTypes' ).returns( wbDataTypes );\n\t\tglobal.mw.config.get.withArgs( 'wbmiPropertyTypes' ).returns( wbmiPropertyTypes );\n\n\t\thelpers.registerWbSerialization();\n\t},\n\tafterEach: function () {\n\t\tsandboxes.wikibase.restore();\n\t\tmockery.disable();\n\t\tmodule.exports.mediawiki.afterEach();\n\t}\n} );\n\nmodule.exports.mediainfo = Object.assign( {}, module.exports.ooui, module.exports.wikibase, {\n\tbeforeEach: function () {\n\t\t// jQuery, OOUI, MediaWiki Wikibase are requirements for MediaInfo\n\t\tmodule.exports.ooui.beforeEach();\n\t\tmodule.exports.wikibase.beforeEach();\n\n\t\tsandboxes.mediainfo = sinon.createSandbox();\n\n\t\t// the captions panel needs the ULS\n\t\thelpers.requireULS();\n\n\t\t// make sure that mediainfo modules (usually exposed via RL)\n\t\t// can be required, and templates are known\n\t\thelpers.registerModules();\n\t\thelpers.registerTemplates();\n\t},\n\tafterEach: function () {\n\t\thelpers.deregisterModules();\n\t\tsandboxes.mediainfo.restore();\n\t\tmodule.exports.wikibase.afterEach();\n\t\tmodule.exports.ooui.afterEach();\n\t}\n} );\n\nmodule.exports.kartographer = Object.assign( {}, module.exports.mediainfo, {\n\tbeforeEach: function () {\n\t\tconst loaderStub = sinon.stub();\n\n\t\tmodule.exports.mediainfo.beforeEach();\n\t\tsandboxes.kartographer = sinon.createSandbox();\n\n\t\tloaderStub.withArgs( 'ext.kartographer.box' ).returns( {\n\t\t\tmap: sinon.stub().returns( {\n\t\t\t\tinvalidateSize: sinon.stub(),\n\t\t\t\ton: sinon.stub(),\n\t\t\t\toff: sinon.stub()\n\t\t\t} )\n\t\t} );\n\n\t\tloaderStub.withArgs( 'ext.kartographer.editing' ).returns( {\n\t\t\tgetKartographerLayer: sinon.stub().returns( {\n\t\t\t\tsetGeoJSON: sinon.stub(),\n\t\t\t\tclearLayers: sinon.stub()\n\t\t\t} )\n\t\t} );\n\n\t\tglobal.mw.loader.using.returns(\n\t\t\t$.Deferred().resolve( loaderStub ).promise()\n\t\t);\n\n\t\tglobal.MutationObserver = function () {};\n\t\tglobal.MutationObserver.prototype = {\n\t\t\tobserve: sinon.stub()\n\t\t};\n\t},\n\n\tafterEach: function () {\n\t\tsandboxes.kartographer.restore();\n\t\tmodule.exports.mediawiki.afterEach();\n\t}\n} );\n","usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/phpunit/data/entities/M1.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/tests/phpunit/data/entities_search/cat.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/tests/phpunit/data/entities_search/dog.json","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/src/repo/tests/selenium/pageobjects/edit.page.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/pageobjects/file.page.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/pageobjects/login.page.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/pageobjects/upload.page.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/specs_betacommons/loggedInUserCanEdit.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/specs_betacommons/loggedInUserCanUpload.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/specs_betacommons/structuredDataOnFilepage.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/support/js-image-generator.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]},{"filePath":"/src/repo/tests/selenium/wdio.conf.js","messages":[],"errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[{"ruleId":"no-new-require","replacedBy":[]},{"ruleId":"no-process-exit","replacedBy":[]}]}]

$ ./node_modules/.bin/grunt stylelint
Running "stylelint:all" (stylelint) task

resources/filepage/styles/mediainfo-filepage-captionspanel.less
 52:14  ✖  Unexpected unit "rem"   unit-disallowed-list

resources/filepage/styles/mediainfo-filepage-mixins.less
 31:15  ✖  Unexpected unit "rem"   unit-disallowed-list

resources/mediasearch-vue/components/EmptyState.less
 7:13  ✖  Unexpected unit "rem"   unit-disallowed-list

resources/mediasearch-vue/components/EndOfResults.less
 7:13  ✖  Unexpected unit "rem"   unit-disallowed-list

resources/mediasearch-vue/components/NoResults.less
 8:13  ✖  Unexpected unit "rem"   unit-disallowed-list

resources/statements/ItemWidget.less
 13:9   ✖  Unexpected unit "rem"   unit-disallowed-list
 25:12  ✖  Unexpected unit "rem"   unit-disallowed-list


⚠ 7 warnings

Warning: Task "stylelint:all" failed. Use --force to continue.

Aborted due to warnings.

$ ./node_modules/.bin/stylelint resources/mediasearch-vue/components/results/OtherResult.less resources/mediasearch-vue/components/base/CopyTextLayout.less resources/statements/QualifierWidget.less resources/mediasearch-vue/components/ConceptChips.less resources/statements/inputs/TimeInputWidget.less resources/statements/inputs/MultiTypeInputWrapperWidget.less resources/mediasearch-vue/components/base/Icon.less resources/mediasearch-vue/components/EmptyState.less resources/mediasearch-vue/components/base/Select.less resources/mediasearch-vue/components/base/Message.less resources/mediainfo-variables.less resources/mediasearch-vue/components/base/Tab.less resources/filepage/styles/mediainfo-filepage-protectionmsgwidget.less resources/statements/inputs/QuantityInputWidget.less resources/mediasearch-vue/components/base/LookupResults.less resources/mediasearch-vue/components/base/Player.less resources/mediasearch-vue/components/base/SelectMenu.less resources/statements/AddPropertyWidget.less resources/statements/StatementWidget.less resources/statements/inputs/GlobeCoordinateInputWidget.less resources/mediasearch-vue/components/QuickView.less resources/statements/ConstraintsReportHandlerElement.less resources/mediasearch-vue/components/base/Button.less resources/filepage/styles/mediainfo-filepage.less resources/mediasearch-vue/components/base/Image.less resources/mediasearch-vue/components/results/AudioResult.less resources/mediasearch-vue/components/DidYouMean.less resources/mediasearch-vue/components/NoResults.less resources/mediasearch-vue/components/SearchResults.less resources/filepage/styles/mediainfo-filepage-statementpanel.less resources/mediasearch-vue/components/results/PageResult.less resources/mediasearch-vue/components/EndOfResults.less resources/mediasearch-vue/components/base/Dialog.less resources/filepage/styles/mediainfo-filepage-mixins.less resources/statements/LinkNoticeWidget.less resources/mediasearch-vue/components/SearchFilters.less resources/mediasearch-vue/components/base/AutocompleteSearchInput.less resources/mediasearch-vue/components/results/VideoResult.less resources/mediasearch-vue/components/base/Tabs.less resources/mediasearch-vue/components/results/ImageResult.less resources/mediasearch-vue/components/Spinner.less resources/statements/ItemWidget.less resources/search/PropertySuggestionsWidget.less resources/mediasearch-vue/components/App.less resources/statements/inputs/EntityAutocompleteInputWidget.less resources/statements/inputs/InputWidget.less resources/filepage/styles/mediainfo-filepage-captionspanel.less -f json
[{"source":"/src/repo/resources/mediasearch-vue/components/results/OtherResult.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/base/CopyTextLayout.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/statements/QualifierWidget.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/ConceptChips.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/statements/inputs/TimeInputWidget.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/statements/inputs/MultiTypeInputWrapperWidget.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/base/Icon.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/EmptyState.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":true,"warnings":[{"line":7,"column":13,"rule":"unit-disallowed-list","severity":"error","text":"Unexpected unit \"rem\" (unit-disallowed-list)"}]},{"source":"/src/repo/resources/mediasearch-vue/components/base/Select.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/base/Message.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediainfo-variables.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/base/Tab.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/filepage/styles/mediainfo-filepage-protectionmsgwidget.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/statements/inputs/QuantityInputWidget.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/base/LookupResults.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/base/Player.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/base/SelectMenu.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/statements/AddPropertyWidget.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/statements/StatementWidget.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/statements/inputs/GlobeCoordinateInputWidget.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/QuickView.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/statements/ConstraintsReportHandlerElement.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/base/Button.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/filepage/styles/mediainfo-filepage.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/base/Image.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/results/AudioResult.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/DidYouMean.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/NoResults.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":true,"warnings":[{"line":8,"column":13,"rule":"unit-disallowed-list","severity":"error","text":"Unexpected unit \"rem\" (unit-disallowed-list)"}]},{"source":"/src/repo/resources/mediasearch-vue/components/SearchResults.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/filepage/styles/mediainfo-filepage-statementpanel.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/results/PageResult.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/EndOfResults.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":true,"warnings":[{"line":7,"column":13,"rule":"unit-disallowed-list","severity":"error","text":"Unexpected unit \"rem\" (unit-disallowed-list)"}]},{"source":"/src/repo/resources/mediasearch-vue/components/base/Dialog.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/filepage/styles/mediainfo-filepage-mixins.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":true,"warnings":[{"line":31,"column":15,"rule":"unit-disallowed-list","severity":"error","text":"Unexpected unit \"rem\" (unit-disallowed-list)"}]},{"source":"/src/repo/resources/statements/LinkNoticeWidget.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/SearchFilters.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/base/AutocompleteSearchInput.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/results/VideoResult.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/base/Tabs.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/results/ImageResult.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/Spinner.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/statements/ItemWidget.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":true,"warnings":[{"line":13,"column":9,"rule":"unit-disallowed-list","severity":"error","text":"Unexpected unit \"rem\" (unit-disallowed-list)"},{"line":25,"column":12,"rule":"unit-disallowed-list","severity":"error","text":"Unexpected unit \"rem\" (unit-disallowed-list)"}]},{"source":"/src/repo/resources/search/PropertySuggestionsWidget.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/mediasearch-vue/components/App.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/statements/inputs/EntityAutocompleteInputWidget.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/statements/inputs/InputWidget.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":false,"warnings":[]},{"source":"/src/repo/resources/filepage/styles/mediainfo-filepage-captionspanel.less","deprecations":[],"invalidOptionWarnings":[],"parseErrors":[],"errored":true,"warnings":[{"line":52,"column":14,"rule":"unit-disallowed-list","severity":"error","text":"Unexpected unit \"rem\" (unit-disallowed-list)"}]}]
$ npm ci
npm WARN prepare removing existing node_modules/ before installation

> ejs@3.1.3 postinstall /src/repo/node_modules/ejs
> node --harmony ./postinstall.js

Thank you for installing EJS: built with the Jake JavaScript build tool (https://jakejs.com/)


> core-js@3.9.1 postinstall /src/repo/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"

Thank you for using core-js ( https://github.com/zloirock/core-js ) for polyfilling JavaScript standard library!

The project needs your help! Please consider supporting of core-js on Open Collective or Patreon: 
> https://opencollective.com/core-js 
> https://www.patreon.com/zloirock 

Also, the author of core-js ( https://github.com/zloirock ) is looking for a good job -)


> chromedriver@2.46.0 install /src/repo/node_modules/chromedriver
> node install.js

ChromeDriver binary exists. Validating...
ChromeDriver 2.46.628388 (4a34a70827ac54148e092aafb70504c4ea7ae926)

ChromeDriver is already available at '/tmp/2.46/chromedriver/chromedriver'.
Copying to target path /src/repo/node_modules/chromedriver/lib/chromedriver
Fixing file permissions
Done. ChromeDriver binary available at /src/repo/node_modules/chromedriver/lib/chromedriver/chromedriver

> fibers@4.0.3 install /src/repo/node_modules/fibers
> node build.js || nodejs build.js

`linux-x64-64-glibc` exists; testing
Binary is fine; exiting

> wikibase-media-info@0.1.0 install /src/repo
> rm -rf node_modules/mediawiki && git clone -q --depth=1 https://gerrit.wikimedia.org/r/mediawiki/core node_modules/mediawiki

added 1178 packages in 38.263s

$ npm test

> wikibase-media-info@0.1.0 test /src/repo
> grunt test && npm run test:unit

Running "eslint:all" (eslint) task

/src/repo/resources/datamodel/MediaInfo.js
  24:0  warning  Syntax error in namepath: 
[statementGroupSet=new datamodel.StatementGroupSet()]  jsdoc/valid-types

/src/repo/resources/filepage/CaptionsPanel.js
  40:0  warning  Invalid JSDoc tag name "mixins"  jsdoc/check-tag-names

/src/repo/resources/filepage/StatementPanel.js
  18:0  warning  Invalid JSDoc tag name "mixins"  jsdoc/check-tag-names

/src/repo/resources/mediasearch-vue/components/App.vue
  184:28  warning  IntersectionObserverEntry is not supported in Safari 5.1, IE 11  compat/compat

/src/repo/resources/mediasearch-vue/components/base/Image.vue
  52:28  warning  IntersectionObserverEntry is not supported in Safari 5.1, IE 11  compat/compat

/src/repo/resources/mediasearch-vue/components/base/Observer.vue
  52:19  warning  IntersectionObserver is not supported in Safari 5.1, IE 11, android 4.1  compat/compat

/src/repo/resources/mediasearch-vue/components/base/Tabs.vue
  95:28  warning  IntersectionObserverEntry is not supported in Safari 5.1, IE 11  compat/compat

/src/repo/resources/mediasearch-vue/components/results/AudioResult.vue
  16:1  warning  This line has a length of 119. Maximum allowed is 100  max-len

/src/repo/resources/mediasearch-vue/components/results/PageResult.vue
  17:23  warning  'v-html' directive can lead to XSS attack  vue/no-v-html

/src/repo/resources/mediasearch-vue/components/results/VideoResult.vue
  24:1  warning  This line has a length of 123. Maximum allowed is 100  max-len

/src/repo/resources/mediasearch-vue/components/SearchFilters.vue
  110:28  warning  IntersectionObserverEntry is not supported in Safari 5.1, IE 11  compat/compat

/src/repo/resources/statements/inputs/GlobeCoordinateInputWidget.js
  426:4  warning  MutationObserver.observe() is not supported in Safari 5.1            compat/compat
  426:4  warning  MutationObserver is not supported in Safari 5.1, iOS Safari 6.0-6.1  compat/compat

/src/repo/resources/statements/ItemWidget.js
  448:4  warning  MutationObserver.observe() is not supported in Safari 5.1            compat/compat
  448:4  warning  MutationObserver is not supported in Safari 5.1, iOS Safari 6.0-6.1  compat/compat

/src/repo/tests/node-qunit/mediainfo/base/ComponentWidget.test.js
  9:34  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

/src/repo/tests/node-qunit/mediainfo/base/DOMLessGroupWidget.test.js
  10:37  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

/src/repo/tests/node-qunit/mediainfo/filepage/CaptionsPanel.test.js
  13:68  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

/src/repo/tests/node-qunit/mediainfo/filepage/StatementPanel.test.js
  14:71  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

/src/repo/tests/node-qunit/mediainfo/mediainfo.template.mustache+dom.test.js
  7:50  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

/src/repo/tests/node-qunit/mediainfo/statements/LinkNoticeWidget.test.js
  9:35  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

/src/repo/tests/node-qunit/support/helpers.js
  103:14  error  Map is not supported in Safari 5.1, iOS Safari 6.0-6.1  compat/compat

/src/repo/tests/node-qunit/support/hooks.js
   32:23  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat
   52:28  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat
   77:27  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat
  105:28  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat
  129:31  error  Object.assign() is not supported in Safari 5.1, iOS Safari 6.0-6.1, IE 11  compat/compat

✖ 27 problems (12 errors, 15 warnings)

Warning: Task "eslint:all" failed. Use --force to continue.

Aborted due to warnings.
npm ERR! Test failed.  See above for more details.

Traceback (most recent call last):
  File "/venv/lib/python3.7/site-packages/libup-0.0.1-py3.7.egg/libup/ng.py", line 1188, in main
    libup.run(args.repo, args.output, args.branch)
  File "/venv/lib/python3.7/site-packages/libup-0.0.1-py3.7.egg/libup/ng.py", line 1130, in run
    self.npm_upgrade(plan)
  File "/venv/lib/python3.7/site-packages/libup-0.0.1-py3.7.egg/libup/ng.py", line 857, in npm_upgrade
    self.npm_test()
  File "/venv/lib/python3.7/site-packages/libup-0.0.1-py3.7.egg/libup/ng.py", line 240, in npm_test
    self.check_call(['npm', 'test'])
  File "/venv/lib/python3.7/site-packages/libup-0.0.1-py3.7.egg/libup/shell.py", line 38, in check_call
    res.check_returncode()
  File "/usr/lib/python3.7/subprocess.py", line 428, in check_returncode
    self.stderr)
subprocess.CalledProcessError: Command '['npm', 'test']' returned non-zero exit status 1.

composer dependencies

Dependencies
Development dependencies

npm dependencies

Development dependencies

Logs

Source code is licensed under the AGPL.