summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--RELEASE-NOTES-1.4214
-rw-r--r--autoload.php11
-rw-r--r--composer.json4
-rw-r--r--docs/config-schema.yaml26
-rw-r--r--includes/Category/CategoryViewer.php2
-rw-r--r--includes/CommentFormatter/CommentParser.php4
-rw-r--r--includes/CommentFormatter/CommentParserFactory.php2
-rw-r--r--includes/GlobalFunctions.php2
-rw-r--r--includes/MainConfigSchema.php26
-rw-r--r--includes/MediaWikiServices.php10
-rw-r--r--includes/Output/OutputPage.php2
-rw-r--r--includes/Permissions/PermissionManager.php2
-rw-r--r--includes/Permissions/RestrictionStore.php2
-rw-r--r--includes/ResourceLoader/CodexModule.php9
-rw-r--r--includes/ResourceLoader/UserModule.php3
-rw-r--r--includes/ResourceLoader/UserStylesModule.php3
-rw-r--r--includes/Rest/Handler.php5
-rw-r--r--includes/Rest/Handler/Helper/HtmlInputTransformHelper.php2
-rw-r--r--includes/Rest/Handler/Helper/HtmlOutputHelper.php7
-rw-r--r--includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php63
-rw-r--r--includes/Rest/Handler/ParsoidHandler.php33
-rw-r--r--includes/Rest/RequestBase.php30
-rw-r--r--includes/Rest/RequestData.php17
-rw-r--r--includes/Rest/RequestInterface.php8
-rw-r--r--includes/Rest/i18n/de.json3
-rw-r--r--includes/Rest/i18n/en.json3
-rw-r--r--includes/Rest/i18n/he.json3
-rw-r--r--includes/Rest/i18n/qqq.json3
-rw-r--r--includes/ServiceWiring.php9
-rw-r--r--includes/actions/RawAction.php2
-rw-r--r--includes/api/ApiPageSet.php3
-rw-r--r--includes/api/ApiParse.php1
-rw-r--r--includes/api/ApiQuery.php1
-rw-r--r--includes/api/ApiQueryAllLinks.php1
-rw-r--r--includes/api/ApiQueryAllPages.php1
-rw-r--r--includes/api/ApiQueryContributors.php14
-rw-r--r--includes/api/ApiQueryUserInfo.php4
-rw-r--r--includes/api/ApiQueryUsers.php1
-rw-r--r--includes/api/ApiQueryWatchlist.php1
-rw-r--r--includes/api/ApiQueryWatchlistRaw.php1
-rw-r--r--includes/api/ApiUpload.php2
-rw-r--r--includes/api/i18n/de.json3
-rw-r--r--includes/api/i18n/en.json3
-rw-r--r--includes/api/i18n/he.json3
-rw-r--r--includes/api/i18n/nb.json3
-rw-r--r--includes/api/i18n/qqq.json3
-rw-r--r--includes/block/DatabaseBlockStore.php76
-rw-r--r--includes/cache/BacklinkCache.php11
-rw-r--r--includes/cache/FileCacheBase.php7
-rw-r--r--includes/cache/GenderCache.php5
-rw-r--r--includes/cache/HTMLCacheUpdater.php (renamed from includes/cache/HtmlCacheUpdater.php)16
-rw-r--r--includes/cache/Hook/HtmlCacheUpdaterAppendUrlsHook.php2
-rw-r--r--includes/cache/LinkBatch.php9
-rw-r--r--includes/cache/LinkCache.php11
-rw-r--r--includes/cache/UserCache.php6
-rw-r--r--includes/changes/CategoryMembershipChange.php1
-rw-r--r--includes/changes/RecentChange.php28
-rw-r--r--includes/content/ContentHandler.php3
-rw-r--r--includes/content/Renderer/ContentRenderer.php2
-rw-r--r--includes/content/WikitextContentHandler.php16
-rw-r--r--includes/deferred/CdnCacheUpdate.php2
-rw-r--r--includes/deferred/LinksUpdate/LinksUpdate.php2
-rw-r--r--includes/editpage/EditPage.php4
-rw-r--r--includes/editpage/TextConflictHelper.php94
-rw-r--r--includes/filerepo/FileRepo.php2
-rw-r--r--includes/installer/i18n/qqq.json2
-rw-r--r--includes/jobqueue/JobRunner.php1
-rw-r--r--includes/jobqueue/jobs/PublishStashedFileJob.php166
-rw-r--r--includes/jobqueue/jobs/UploadJobTrait.php280
-rw-r--r--includes/libs/ParamValidator/i18n/hu.json43
-rw-r--r--includes/libs/filebackend/FileOpBatch.php2
-rw-r--r--includes/libs/filebackend/fileop/CopyFileOp.php29
-rw-r--r--includes/libs/filebackend/fileop/CreateFileOp.php19
-rw-r--r--includes/libs/filebackend/fileop/DeleteFileOp.php15
-rw-r--r--includes/libs/filebackend/fileop/DescribeFileOp.php23
-rw-r--r--includes/libs/filebackend/fileop/FileOp.php172
-rw-r--r--includes/libs/filebackend/fileop/FileStatePredicates.php142
-rw-r--r--includes/libs/filebackend/fileop/MoveFileOp.php33
-rw-r--r--includes/libs/filebackend/fileop/StoreFileOp.php25
-rw-r--r--includes/libs/objectcache/BagOStuff.php22
-rw-r--r--includes/libs/objectcache/MediumSpecificBagOStuff.php62
-rw-r--r--includes/linker/LinkRenderer.php2
-rw-r--r--includes/linker/LinkRendererFactory.php2
-rw-r--r--includes/objectcache/ObjectCache.php2
-rw-r--r--includes/page/Article.php3
-rw-r--r--includes/page/PageSelectQueryBuilder.php2
-rw-r--r--includes/page/PageStore.php2
-rw-r--r--includes/page/PageStoreFactory.php2
-rw-r--r--includes/page/ParserOutputAccess.php77
-rw-r--r--includes/parser/DateFormatter.php2
-rw-r--r--includes/parser/LinkHolderArray.php1
-rw-r--r--includes/poolcounter/PoolWorkArticleView.php19
-rw-r--r--includes/revisiondelete/RevDelArchiveList.php5
-rw-r--r--includes/revisiondelete/RevDelArchivedFileList.php5
-rw-r--r--includes/revisiondelete/RevDelFileList.php9
-rw-r--r--includes/revisiondelete/RevDelRevisionList.php9
-rw-r--r--includes/specials/SpecialEditWatchlist.php2
-rw-r--r--includes/specials/SpecialListFiles.php2
-rw-r--r--includes/specials/SpecialPrefixIndex.php2
-rw-r--r--includes/specials/SpecialProtectedPages.php2
-rw-r--r--includes/specials/SpecialUndelete.php2
-rw-r--r--includes/specials/SpecialUploadStash.php4
-rw-r--r--includes/specials/pagers/ImageListPager.php2
-rw-r--r--includes/specials/pagers/ProtectedPagesPager.php2
-rw-r--r--includes/title/MediaWikiTitleCodec.php2
-rw-r--r--includes/title/Title.php6
-rw-r--r--includes/upload/UploadFromUrl.php20
-rw-r--r--includes/user/User.php2
-rw-r--r--jsdoc.js22
-rw-r--r--languages/i18n/acm.json50
-rw-r--r--languages/i18n/alt.json14
-rw-r--r--languages/i18n/av.json3
-rw-r--r--languages/i18n/ba.json10
-rw-r--r--languages/i18n/ca.json11
-rw-r--r--languages/i18n/ce.json4
-rw-r--r--languages/i18n/chn.json234
-rw-r--r--languages/i18n/cv.json1
-rw-r--r--languages/i18n/de.json1
-rw-r--r--languages/i18n/en.json1
-rw-r--r--languages/i18n/et.json18
-rw-r--r--languages/i18n/ha.json1
-rw-r--r--languages/i18n/he.json1
-rw-r--r--languages/i18n/hif-latn.json2
-rw-r--r--languages/i18n/hu.json40
-rw-r--r--languages/i18n/ia.json10
-rw-r--r--languages/i18n/inh.json8
-rw-r--r--languages/i18n/it.json1
-rw-r--r--languages/i18n/mk.json1
-rw-r--r--languages/i18n/nb.json1
-rw-r--r--languages/i18n/nl.json1
-rw-r--r--languages/i18n/nso.json2
-rw-r--r--languages/i18n/pl.json1
-rw-r--r--languages/i18n/pnb.json2
-rw-r--r--languages/i18n/qqq.json9
-rw-r--r--languages/i18n/ru.json6
-rw-r--r--languages/i18n/sa.json2
-rw-r--r--languages/i18n/sh.json1
-rw-r--r--languages/i18n/si.json2
-rw-r--r--languages/i18n/sr-ec.json9
-rw-r--r--languages/i18n/tr.json7
-rw-r--r--languages/i18n/ur.json2
-rw-r--r--languages/i18n/war.json2
-rw-r--r--languages/i18n/zh-hans.json6
-rw-r--r--languages/i18n/zh-hant.json1
-rw-r--r--languages/messages/MessagesCs.php14
-rw-r--r--maintenance/benchmarks/benchmarkParse.php1
-rw-r--r--maintenance/getLagTimes.php8
-rw-r--r--maintenance/updateCollation.php39
-rw-r--r--maintenance/userOptions.php2
-rw-r--r--resources/Resources.php2
-rw-r--r--resources/lib/codex/codex-search.cjs1
-rw-r--r--resources/lib/codex/codex-search.js2126
-rw-r--r--resources/lib/codex/codex-search.style-experimental-rtl.css1
-rw-r--r--resources/lib/codex/codex-search.style-experimental.css1
-rw-r--r--resources/lib/codex/codex-search.style-legacy-rtl.css1
-rw-r--r--resources/lib/codex/codex-search.style-legacy.css1
-rw-r--r--resources/lib/codex/codex-search.style-rtl.css1
-rw-r--r--resources/lib/codex/codex-search.style.css1
-rw-r--r--resources/lib/foreign-resources.yaml9
-rw-r--r--resources/lib/oojs-router/oojs-router.js10
-rw-r--r--resources/src/mediawiki.ForeignStructuredUpload.BookletLayout/BookletLayout.js20
-rw-r--r--resources/src/mediawiki.ForeignStructuredUpload.BookletLayout/ForeignStructuredUpload.js3
-rw-r--r--resources/src/mediawiki.Upload.BookletLayout/BookletLayout.js126
-rw-r--r--resources/src/mediawiki.Upload.BookletLayout/mw.widgets.StashedFileWidget.js14
-rw-r--r--resources/src/mediawiki.Upload.Dialog.js29
-rw-r--r--resources/src/mediawiki.Upload.js62
-rw-r--r--resources/src/mediawiki.Uri/Uri.js58
-rw-r--r--resources/src/mediawiki.api/index.js40
-rw-r--r--resources/src/mediawiki.base/mediawiki.base.js21
-rw-r--r--resources/src/mediawiki.feedback/feedback.js17
-rw-r--r--resources/src/mediawiki.htmlform.ooui/Element.js30
-rw-r--r--resources/src/mediawiki.htmlform/htmlform.js8
-rw-r--r--resources/src/mediawiki.htmlform/selectorother.js10
-rw-r--r--resources/src/mediawiki.jqueryMsg/mediawiki.jqueryMsg.js2
-rw-r--r--resources/src/mediawiki.page.ready/checkboxHack.js23
-rw-r--r--resources/src/mediawiki.page.ready/ready.js2
-rw-r--r--resources/src/mediawiki.page.ready/toggleAllCollapsibles.js48
-rw-r--r--resources/src/mediawiki.rcfilters/Controller.js25
-rw-r--r--resources/src/mediawiki.rcfilters/HighlightColors.js2
-rw-r--r--resources/src/mediawiki.rcfilters/UriProcessor.js10
-rw-r--r--resources/src/mediawiki.rcfilters/dm/ChangesListViewModel.js25
-rw-r--r--resources/src/mediawiki.rcfilters/dm/FilterGroup.js54
-rw-r--r--resources/src/mediawiki.rcfilters/dm/FilterItem.js13
-rw-r--r--resources/src/mediawiki.rcfilters/dm/FiltersViewModel.js35
-rw-r--r--resources/src/mediawiki.rcfilters/dm/ItemModel.js37
-rw-r--r--resources/src/mediawiki.rcfilters/dm/SavedQueriesModel.js23
-rw-r--r--resources/src/mediawiki.rcfilters/dm/SavedQueryItemModel.js11
-rw-r--r--resources/src/mediawiki.rcfilters/mw.rcfilters.js10
-rw-r--r--resources/src/mediawiki.rcfilters/ui/ChangesLimitAndDateButtonWidget.js6
-rw-r--r--resources/src/mediawiki.rcfilters/ui/ChangesLimitPopupWidget.js14
-rw-r--r--resources/src/mediawiki.rcfilters/ui/ChangesListWrapperWidget.js4
-rw-r--r--resources/src/mediawiki.rcfilters/ui/CheckboxInputWidget.js9
-rw-r--r--resources/src/mediawiki.rcfilters/ui/DatePopupWidget.js9
-rw-r--r--resources/src/mediawiki.rcfilters/ui/FilterItemHighlightButton.js4
-rw-r--r--resources/src/mediawiki.rcfilters/ui/FilterMenuHeaderWidget.js6
-rw-r--r--resources/src/mediawiki.rcfilters/ui/FilterMenuOptionWidget.js4
-rw-r--r--resources/src/mediawiki.rcfilters/ui/FilterMenuSectionOptionWidget.js6
-rw-r--r--resources/src/mediawiki.rcfilters/ui/FilterTagItemWidget.js2
-rw-r--r--resources/src/mediawiki.rcfilters/ui/FilterTagMultiselectWidget.js17
-rw-r--r--resources/src/mediawiki.rcfilters/ui/FilterWrapperWidget.js12
-rw-r--r--resources/src/mediawiki.rcfilters/ui/FormWrapperWidget.js2
-rw-r--r--resources/src/mediawiki.rcfilters/ui/HighlightColorPickerWidget.js9
-rw-r--r--resources/src/mediawiki.rcfilters/ui/HighlightPopupWidget.js2
-rw-r--r--resources/src/mediawiki.rcfilters/ui/ItemMenuOptionWidget.js4
-rw-r--r--resources/src/mediawiki.rcfilters/ui/LiveUpdateButtonWidget.js4
-rw-r--r--resources/src/mediawiki.rcfilters/ui/MainWrapperWidget.js17
-rw-r--r--resources/src/mediawiki.rcfilters/ui/MarkSeenButtonWidget.js4
-rw-r--r--resources/src/mediawiki.rcfilters/ui/MenuSelectWidget.js11
-rw-r--r--resources/src/mediawiki.rcfilters/ui/RcTopSectionWidget.js4
-rw-r--r--resources/src/mediawiki.rcfilters/ui/RclTargetPageWidget.js4
-rw-r--r--resources/src/mediawiki.rcfilters/ui/RclToOrFromWidget.js4
-rw-r--r--resources/src/mediawiki.rcfilters/ui/RclTopSectionWidget.js4
-rw-r--r--resources/src/mediawiki.rcfilters/ui/SaveFiltersPopupButtonWidget.js2
-rw-r--r--resources/src/mediawiki.rcfilters/ui/SavedLinksListItemWidget.js21
-rw-r--r--resources/src/mediawiki.rcfilters/ui/SavedLinksListWidget.js7
-rw-r--r--resources/src/mediawiki.rcfilters/ui/TagItemWidget.js4
-rw-r--r--resources/src/mediawiki.rcfilters/ui/ValuePickerWidget.js11
-rw-r--r--resources/src/mediawiki.rcfilters/ui/ViewSwitchWidget.js4
-rw-r--r--resources/src/mediawiki.rcfilters/ui/WatchlistTopSectionWidget.js4
-rw-r--r--resources/src/mediawiki.rcfilters/utils.js2
-rw-r--r--resources/src/mediawiki.skinning/i18n-headings.less5
-rw-r--r--resources/src/mediawiki.special.preferences.ooui/nav.js3
-rw-r--r--resources/src/mediawiki.special.preferences.ooui/tabs.js38
-rw-r--r--resources/src/mediawiki.widgets.datetime/CalendarWidget.js12
-rw-r--r--resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js10
-rw-r--r--resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js16
-rw-r--r--resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js14
-rw-r--r--resources/src/mediawiki.widgets/MediaSearch/mw.widgets.APIResultsProvider.js12
-rw-r--r--resources/src/mediawiki.widgets/MediaSearch/mw.widgets.APIResultsQueue.js4
-rw-r--r--resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResourceProvider.js2
-rw-r--r--resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResourceQueue.js2
-rw-r--r--resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResultWidget.js8
-rw-r--r--resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaSearchQueue.js2
-rw-r--r--resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaUserUploadsQueue.js2
-rw-r--r--resources/src/mediawiki.widgets/Table/mw.widgets.RowWidget.js14
-rw-r--r--resources/src/mediawiki.widgets/Table/mw.widgets.RowWidgetModel.js12
-rw-r--r--resources/src/mediawiki.widgets/Table/mw.widgets.TableWidget.js10
-rw-r--r--resources/src/mediawiki.widgets/Table/mw.widgets.TableWidgetModel.js23
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.js8
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.CategoryMultiselectWidget.js6
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.CategoryTagItemWidget.js4
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.CheckMatrixWidget.js12
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.ComplexNamespaceInputWidget.js16
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.ComplexTitleInputWidget.js4
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.CopyTextLayout.js6
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js24
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.NamespaceInputWidget.js6
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.SearchInputWidget.js4
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.SelectWithInputWidget.js8
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.SizeFilterWidget.js6
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.TagMultiselectWidget.js2
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.js4
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.TitleOptionWidget.js20
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js40
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.ToggleSwitchWidget.js2
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.UserInputWidget.js4
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.UsersMultiselectWidget.js16
-rw-r--r--resources/src/startup/mediawiki.loader.js7
-rw-r--r--tests/parser/ParserTestRunner.php3
-rw-r--r--tests/parser/parserTests.txt15
-rw-r--r--tests/phpunit/MediaWikiTestCaseTrait.php24
-rw-r--r--tests/phpunit/bootstrap.php4
-rw-r--r--tests/phpunit/includes/Output/OutputPageTest.php2
-rw-r--r--tests/phpunit/includes/OutputTransform/OutputTransformStageTestBase.php2
-rw-r--r--tests/phpunit/includes/api/query/ApiQueryUserInfoTest.php48
-rw-r--r--tests/phpunit/includes/cache/GenderCacheTest.php2
-rw-r--r--tests/phpunit/includes/cache/LinkBatchTest.php3
-rw-r--r--tests/phpunit/includes/cache/LinkCacheTest.php1
-rw-r--r--tests/phpunit/includes/filebackend/FileBackendIntegrationTest.php8
-rw-r--r--tests/phpunit/includes/linker/LinkRendererTest.php1
-rw-r--r--tests/phpunit/includes/logging/LogFormatterTestCase.php2
-rw-r--r--tests/phpunit/includes/page/ParserOutputAccessTest.php304
-rw-r--r--tests/phpunit/includes/title/MediaWikiTitleCodecTest.php1
-rw-r--r--tests/phpunit/includes/title/TitleTest.php1
-rw-r--r--tests/phpunit/includes/upload/UploadFromUrlTest.php11
-rw-r--r--tests/phpunit/includes/watcheditem/WatchedItemStoreUnitTest.php2
-rw-r--r--tests/phpunit/integration/includes/Permissions/RestrictionStoreTest.php2
-rw-r--r--tests/phpunit/integration/includes/Rest/Handler/Helper/HtmlOutputRendererHelperTest.php131
-rw-r--r--tests/phpunit/integration/includes/Rest/Handler/ParsoidHandlerTest.php39
-rw-r--r--tests/phpunit/integration/includes/block/DatabaseBlockStoreTest.php14
-rw-r--r--tests/phpunit/integration/includes/cache/HtmlCacheUpdaterIntegrationTest.php11
-rw-r--r--tests/phpunit/maintenance/GetLagTimesTest.php3
-rw-r--r--tests/phpunit/mocks/DummyServicesTrait.php2
-rw-r--r--tests/phpunit/unit/includes/CommentFormatter/CommentParserFactoryTest.php2
-rw-r--r--tests/phpunit/unit/includes/Permissions/PermissionManagerTest.php2
-rw-r--r--tests/phpunit/unit/includes/Permissions/RestrictionStoreTest.php2
-rw-r--r--tests/phpunit/unit/includes/Rest/Handler/HandlerTest.php12
-rw-r--r--tests/phpunit/unit/includes/Rest/RequestBaseTest.php30
-rw-r--r--tests/phpunit/unit/includes/Rest/RequestDataTest.php43
-rw-r--r--tests/phpunit/unit/includes/cache/HTMLCacheUpdaterTest.php (renamed from tests/phpunit/unit/includes/cache/HtmlCacheUpdaterTest.php)7
-rw-r--r--tests/phpunit/unit/includes/cache/LinkBatchFactoryTest.php3
-rw-r--r--tests/phpunit/unit/includes/libs/objectcache/HashBagOStuffTest.php50
-rw-r--r--tests/phpunit/unit/includes/linker/LinkRendererFactoryTest.php1
-rw-r--r--tests/phpunit/unit/includes/page/PageStoreFactoryTest.php2
-rw-r--r--tests/phpunit/unit/includes/parser/Parsoid/LanguageVariantConverterUnitTest.php48
295 files changed, 3097 insertions, 3687 deletions
diff --git a/RELEASE-NOTES-1.42 b/RELEASE-NOTES-1.42
index 9e82b3549467..e14accedd8cf 100644
--- a/RELEASE-NOTES-1.42
+++ b/RELEASE-NOTES-1.42
@@ -110,6 +110,7 @@ For notes on 1.41.x and older releases, see HISTORY.
==== Changed external libraries ====
* Updated OOUI from v0.48.1 to v0.49.0.
+* Updated OOjs Router from 0.3.0 to 0.4.0.
* Updated codex, codex-design-tokens and codex-icons
from v1.0.0 to v1.3.3.
* Updated wikimedia/minify from 2.5.1 to 2.7.0.
@@ -132,7 +133,9 @@ For notes on 1.41.x and older releases, see HISTORY.
* Updated doctrine/dbal from 3.4.2 to 3.7.2.
* Updated doctrine/sql-formatter from 1.1.1 to 1.1.3.
* Updated grunt-banana-checker from 0.11.0 to 0.11.1.
+* Updated mediawiki/mediawiki-codesniffer from 42.0.0 to 43.0.0.
* Updated mediawiki/mediawiki-phan-config from 0.13.0 to 0.14.0.
+* Updated phpunit/phpunit from 9.5.28 to 9.6.16.
* Updated psy/psysh from ^0.11.1 to ^0.12.0.
* Updated seld/jsonlint from 1.8.3 to 1.10.1.
* Updated wikimedia/testing-access-wrapper from 2.0.0 to 3.0.0.
@@ -423,6 +426,14 @@ because of Phabricator reports.
In this release of MediaWiki, XYZ classes now have a namespace and XYZ do
not yet (XYZ% done, up from 63% in MediaWiki 1.41.0). The following have newly
been moved:
+ - MediaWiki\Cache:
+ - BacklinkCache
+ - FileCacheBase
+ - GenderCache
+ - HTMLCacheUpdater (and fix case for consistency with HTMLFileCache)
+ - LinkBatch
+ - LinkCache
+ - UserCache
- MediaWiki\Context:
- ContextSource
- DerivativeContextSource
@@ -669,6 +680,9 @@ because of Phabricator reports.
has been hard deprecated
* IMaintainableDatabase::truncate() has been deprecated. Use truncateTable()
instead.
+* TextConflictHelper->incrementStatsByUserEdits() is now deprecated. The
+ action this function previously handled should be moved into
+ incrementConflictStats() and incrementResolvedStats().
* …
=== Other changes in 1.42 ===
diff --git a/autoload.php b/autoload.php
index 64c9f6a4ddaa..c99d39545821 100644
--- a/autoload.php
+++ b/autoload.php
@@ -494,6 +494,7 @@ $wgAutoloadLocalClasses = [
'FileOpBatch' => __DIR__ . '/includes/libs/filebackend/FileOpBatch.php',
'FileOpPerfTest' => __DIR__ . '/maintenance/fileOpPerfTest.php',
'FileRepo' => __DIR__ . '/includes/filerepo/FileRepo.php',
+ 'FileStatePredicates' => __DIR__ . '/includes/libs/filebackend/fileop/FileStatePredicates.php',
'FindBadBlobs' => __DIR__ . '/maintenance/findBadBlobs.php',
'FindClasses' => __DIR__ . '/maintenance/findClasses.php',
'FindDeprecated' => __DIR__ . '/maintenance/findDeprecated.php',
@@ -612,7 +613,7 @@ $wgAutoloadLocalClasses = [
'HistoryPager' => __DIR__ . '/includes/actions/pagers/HistoryPager.php',
'Html' => __DIR__ . '/includes/Html/Html.php',
'HtmlArmor' => __DIR__ . '/includes/libs/HtmlArmor.php',
- 'HtmlCacheUpdater' => __DIR__ . '/includes/cache/HtmlCacheUpdater.php',
+ 'HtmlCacheUpdater' => __DIR__ . '/includes/cache/HTMLCacheUpdater.php',
'HtmlFileCacheUpdate' => __DIR__ . '/includes/deferred/HtmlFileCacheUpdate.php',
'HttpError' => __DIR__ . '/includes/exception/HttpError.php',
'HttpStatus' => __DIR__ . '/includes/libs/HttpStatus.php',
@@ -943,8 +944,12 @@ $wgAutoloadLocalClasses = [
'MediaWiki\\Block\\UnblockUser' => __DIR__ . '/includes/block/UnblockUser.php',
'MediaWiki\\Block\\UnblockUserFactory' => __DIR__ . '/includes/block/UnblockUserFactory.php',
'MediaWiki\\Block\\UserBlockCommandFactory' => __DIR__ . '/includes/block/UserBlockCommandFactory.php',
+ 'MediaWiki\\Cache\\BacklinkCache' => __DIR__ . '/includes/cache/BacklinkCache.php',
'MediaWiki\\Cache\\BacklinkCacheFactory' => __DIR__ . '/includes/cache/BacklinkCacheFactory.php',
'MediaWiki\\Cache\\CacheKeyHelper' => __DIR__ . '/includes/cache/CacheKeyHelper.php',
+ 'MediaWiki\\Cache\\FileCacheBase' => __DIR__ . '/includes/cache/FileCacheBase.php',
+ 'MediaWiki\\Cache\\GenderCache' => __DIR__ . '/includes/cache/GenderCache.php',
+ 'MediaWiki\\Cache\\HTMLCacheUpdater' => __DIR__ . '/includes/cache/HTMLCacheUpdater.php',
'MediaWiki\\Cache\\Hook\\BacklinkCacheGetConditionsHook' => __DIR__ . '/includes/cache/Hook/BacklinkCacheGetConditionsHook.php',
'MediaWiki\\Cache\\Hook\\BacklinkCacheGetPrefixHook' => __DIR__ . '/includes/cache/Hook/BacklinkCacheGetPrefixHook.php',
'MediaWiki\\Cache\\Hook\\HTMLFileCache__useFileCacheHook' => __DIR__ . '/includes/cache/Hook/HTMLFileCache__useFileCacheHook.php',
@@ -954,7 +959,10 @@ $wgAutoloadLocalClasses = [
'MediaWiki\\Cache\\Hook\\MessageCacheReplaceHook' => __DIR__ . '/includes/language/Hook/MessageCacheReplaceHook.php',
'MediaWiki\\Cache\\Hook\\MessageCache__getHook' => __DIR__ . '/includes/language/Hook/MessageCache__getHook.php',
'MediaWiki\\Cache\\Hook\\MessagesPreLoadHook' => __DIR__ . '/includes/language/Hook/MessagesPreLoadHook.php',
+ 'MediaWiki\\Cache\\LinkBatch' => __DIR__ . '/includes/cache/LinkBatch.php',
'MediaWiki\\Cache\\LinkBatchFactory' => __DIR__ . '/includes/cache/LinkBatchFactory.php',
+ 'MediaWiki\\Cache\\LinkCache' => __DIR__ . '/includes/cache/LinkCache.php',
+ 'MediaWiki\\Cache\\UserCache' => __DIR__ . '/includes/cache/UserCache.php',
'MediaWiki\\Category\\CategoriesRdf' => __DIR__ . '/includes/Category/CategoriesRdf.php',
'MediaWiki\\Category\\Category' => __DIR__ . '/includes/Category/Category.php',
'MediaWiki\\Category\\CategoryViewer' => __DIR__ . '/includes/Category/CategoryViewer.php',
@@ -3042,6 +3050,7 @@ $wgAutoloadLocalClasses = [
'UploadFromFile' => __DIR__ . '/includes/upload/UploadFromFile.php',
'UploadFromStash' => __DIR__ . '/includes/upload/UploadFromStash.php',
'UploadFromUrl' => __DIR__ . '/includes/upload/UploadFromUrl.php',
+ 'UploadJobTrait' => __DIR__ . '/includes/jobqueue/jobs/UploadJobTrait.php',
'UploadLogFormatter' => __DIR__ . '/includes/logging/UploadLogFormatter.php',
'UploadRevisionImporter' => __DIR__ . '/includes/import/UploadRevisionImporter.php',
'UploadSourceAdapter' => __DIR__ . '/includes/import/UploadSourceAdapter.php',
diff --git a/composer.json b/composer.json
index be5e069a9316..050a7a2e4670 100644
--- a/composer.json
+++ b/composer.json
@@ -93,12 +93,12 @@
"giorgiosironi/eris": "^0.14.0",
"hamcrest/hamcrest-php": "^2.0",
"johnkary/phpunit-speedtrap": "^4.0",
- "mediawiki/mediawiki-codesniffer": "42.0.0",
+ "mediawiki/mediawiki-codesniffer": "43.0.0",
"mediawiki/mediawiki-phan-config": "0.14.0",
"nikic/php-parser": "^4.10.2",
"php-parallel-lint/php-console-highlighter": "1.0.0",
"php-parallel-lint/php-parallel-lint": "1.3.2",
- "phpunit/phpunit": "9.5.28",
+ "phpunit/phpunit": "9.6.16",
"psy/psysh": "^0.12.0",
"seld/jsonlint": "1.10.1",
"wikimedia/alea": "1.0.0",
diff --git a/docs/config-schema.yaml b/docs/config-schema.yaml
index d0beef3ea992..9b2d239230d3 100644
--- a/docs/config-schema.yaml
+++ b/docs/config-schema.yaml
@@ -317,7 +317,7 @@ config-schema:
TmpDirectory:
default: false
description: |-
- The local filesystem path to a temporary directory. This must not be web accessible.
+ The local filesystem path to a temporary directory. This must not be web-accessible.
When this setting is set to false, its value will automatically be decided
through the first call to wfTempDir(). See that method's implementation for
the actual detection logic.
@@ -325,7 +325,7 @@ config-schema:
this variable directly. Use the global function wfTempDir() instead.
The temporary directory is expected to be shared with other applications,
including other MediaWiki instances (which might not run the same version
- or configution). When storing files here, take care to avoid conflicts
+ or configuration). When storing files here, take care to avoid conflicts
with other instances of MediaWiki. For example, when caching the result
of a computation, the file name should incorporate the input of the
computation so that it cannot be confused for the result of a similar
@@ -1452,7 +1452,7 @@ config-schema:
by hardcoded px in wiki sourcecode.
DirectoryMode:
default: 511
- description: 'Default value for chmoding of new directories.'
+ description: 'Default value for chmod-ing of new directories.'
ResponsiveImages:
default: true
description: |-
@@ -1861,7 +1861,7 @@ config-schema:
- groupLoads: (optional) Array of load ratios, the key is the query group name. A query
may belong to several groups, the most specific group defined here is used.
- flags: (optional) Bit field of properties:
- - DBO_DEFAULT: Transactionalize web requests and use autocommit otherwise
+ - DBO_DEFAULT: Transactional-ize web requests and use autocommit otherwise
- DBO_DEBUG: Equivalent of $wgDebugDumpSql
- DBO_SSL: Use TLS connection encryption if available (deprecated)
- DBO_COMPRESS: Use protocol compression with database connections
@@ -3220,11 +3220,11 @@ config-schema:
default: telephone=no
type: string
description: |-
- Override ability of certains browsers to attempt to autodetect dataformats in pages.
+ Override the ability of certain browsers to attempt to autodetect dataformats in pages.
This is a default feature of many mobile browsers, but can have a lot of false positives,
- where for instance year ranges are confused with phone numbers.
+ where for instance, year ranges are confused with phone numbers.
The default of this setting is to disable telephone number data detection.
- Set BrowserFormatDetection to false to fallback to browser defaults.
+ Set BrowserFormatDetection to false to fallback to the browser defaults.
@since 1.37
@see https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html
SkinMetaTags:
@@ -5706,7 +5706,7 @@ config-schema:
The X-Frame-Options header to send on pages sensitive to clickjacking
attacks, such as edit pages. This prevents those pages from being displayed
in a frame or iframe. The options are:
- - 'DENY': Do not allow framing. This is recommended for most wikis.
+ - 'DENY': Do not allow framing. This is recommended for most wikis.
- 'SAMEORIGIN': Allow framing by pages on the same domain. This can be used
to allow framing within a trusted domain. This is insecure if there
is a page on the same domain which allows framing of arbitrary URLs.
@@ -7394,7 +7394,7 @@ config-schema:
type: object
description: |-
Map of allowed values for the "title=foo&action=<action>" parameter.
- to the corrspeonding handler code.
+ to the corresponding handler code.
See ActionFactory for the syntax. Core defaults are in ActionFactory::CORE_ACTIONS,
anything here overrides that.
DefaultRobotPolicy:
@@ -7786,7 +7786,7 @@ config-schema:
default: 1
description: |-
Number of jobs to perform per request. May be less than one in which case jobs are
- performed probabalistically. If this is zero, jobs will not be done during ordinary
+ performed probabilistically. If this is zero, jobs will not be done during ordinary
apache requests. In this case, maintenance/runJobs.php should be run in loop every
few seconds via a service or cron job. If using a cron job, be sure to handle the
case where the script is already running (e.g. via `/usr/bin/flock -n <lock_file>`).
@@ -7859,14 +7859,14 @@ config-schema:
Mapping of event channels (or channel categories) to EventRelayer configuration.
By setting up a PubSub system (like Kafka) and enabling a corresponding EventRelayer class
that uses it, MediaWiki can broadcast events to all subscribers. Certain features like WAN
- cache purging and CDN cache purging will emit events to this system. Appropriate listers can
- subscribe to the channel and take actions based on the events. For example, a local daemon
+ cache purging and CDN cache purging will emit events to this system. Appropriate listeners
+ can subscribe to the channel and take actions based on the events. For example, a local daemon
can run on each CDN cache node and perform local purges based on the URL purge channel
events.
Some extensions may want to use "channel categories" so that different channels can also
share the same custom relayer instance (e.g. when it's likely to be overridden). They can use
EventRelayerGroup::getRelayer() based on the category but call notify() on various different
- actual channels. One reason for this would be that some system have very different
+ actual channels. One reason for this would be that some systems have very different
performance vs durability needs, so one system (e.g. Kafka) may not be suitable for all
uses.
The 'default' key is for all channels (or channel categories) without an explicit entry
diff --git a/includes/Category/CategoryViewer.php b/includes/Category/CategoryViewer.php
index abcdec63570b..ca87346e3442 100644
--- a/includes/Category/CategoryViewer.php
+++ b/includes/Category/CategoryViewer.php
@@ -29,7 +29,7 @@ use ILanguageConverter;
use ImageGalleryBase;
use ImageGalleryClassNotFoundException;
use InvalidArgumentException;
-use LinkCache;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Context\ContextSource;
use MediaWiki\Context\IContextSource;
use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
diff --git a/includes/CommentFormatter/CommentParser.php b/includes/CommentFormatter/CommentParser.php
index e37e35cdb8ff..9182d890ca03 100644
--- a/includes/CommentFormatter/CommentParser.php
+++ b/includes/CommentFormatter/CommentParser.php
@@ -5,9 +5,9 @@ namespace MediaWiki\CommentFormatter;
use File;
use HtmlArmor;
use Language;
-use LinkBatch;
-use LinkCache;
+use MediaWiki\Cache\LinkBatch;
use MediaWiki\Cache\LinkBatchFactory;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\HookContainer\HookRunner;
use MediaWiki\Linker\Linker;
diff --git a/includes/CommentFormatter/CommentParserFactory.php b/includes/CommentFormatter/CommentParserFactory.php
index a28f07b17e51..69e49766aca6 100644
--- a/includes/CommentFormatter/CommentParserFactory.php
+++ b/includes/CommentFormatter/CommentParserFactory.php
@@ -3,8 +3,8 @@
namespace MediaWiki\CommentFormatter;
use Language;
-use LinkCache;
use MediaWiki\Cache\LinkBatchFactory;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\Linker\LinkRenderer;
use MediaWiki\Title\NamespaceInfo;
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index 4c7c31619c5a..edc56f44f6e3 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -1179,7 +1179,7 @@ function wfEscapeWikiText( $input ): string {
$repl4 = [
'_' => '&#95;', '~' => '&#126;',
"\n" => "&#10;", "\r" => "&#13;",
- "\t" => "&#9;", // "\n\t\n" is treated like "\n\n"
+ "\t" => "&#9;", // "\n\t\n" is treated like "\n\n"
];
// And handle protocols that don't use "://"
diff --git a/includes/MainConfigSchema.php b/includes/MainConfigSchema.php
index c4acd5aab517..0c45119a5848 100644
--- a/includes/MainConfigSchema.php
+++ b/includes/MainConfigSchema.php
@@ -736,7 +736,7 @@ class MainConfigSchema {
];
/**
- * The local filesystem path to a temporary directory. This must not be web accessible.
+ * The local filesystem path to a temporary directory. This must not be web-accessible.
*
* When this setting is set to false, its value will automatically be decided
* through the first call to wfTempDir(). See that method's implementation for
@@ -747,7 +747,7 @@ class MainConfigSchema {
*
* The temporary directory is expected to be shared with other applications,
* including other MediaWiki instances (which might not run the same version
- * or configution). When storing files here, take care to avoid conflicts
+ * or configuration). When storing files here, take care to avoid conflicts
* with other instances of MediaWiki. For example, when caching the result
* of a computation, the file name should incorporate the input of the
* computation so that it cannot be confused for the result of a similar
@@ -2401,7 +2401,7 @@ class MainConfigSchema {
];
/**
- * Default value for chmoding of new directories.
+ * Default value for chmod-ing of new directories.
*/
public const DirectoryMode = [
'default' => 0777, // octal!
@@ -3072,7 +3072,7 @@ class MainConfigSchema {
* may belong to several groups, the most specific group defined here is used.
*
* - flags: (optional) Bit field of properties:
- * - DBO_DEFAULT: Transactionalize web requests and use autocommit otherwise
+ * - DBO_DEFAULT: Transactional-ize web requests and use autocommit otherwise
* - DBO_DEBUG: Equivalent of $wgDebugDumpSql
* - DBO_SSL: Use TLS connection encryption if available (deprecated)
* - DBO_COMPRESS: Use protocol compression with database connections
@@ -5179,12 +5179,12 @@ class MainConfigSchema {
];
/**
- * Override ability of certains browsers to attempt to autodetect dataformats in pages.
+ * Override the ability of certain browsers to attempt to autodetect dataformats in pages.
*
* This is a default feature of many mobile browsers, but can have a lot of false positives,
- * where for instance year ranges are confused with phone numbers.
+ * where for instance, year ranges are confused with phone numbers.
* The default of this setting is to disable telephone number data detection.
- * Set BrowserFormatDetection to false to fallback to browser defaults.
+ * Set BrowserFormatDetection to false to fallback to the browser defaults.
*
* @since 1.37
* @see https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html
@@ -9130,7 +9130,7 @@ class MainConfigSchema {
* attacks, such as edit pages. This prevents those pages from being displayed
* in a frame or iframe. The options are:
*
- * - 'DENY': Do not allow framing. This is recommended for most wikis.
+ * - 'DENY': Do not allow framing. This is recommended for most wikis.
*
* - 'SAMEORIGIN': Allow framing by pages on the same domain. This can be used
* to allow framing within a trusted domain. This is insecure if there
@@ -11855,7 +11855,7 @@ class MainConfigSchema {
/**
* Map of allowed values for the "title=foo&action=<action>" parameter.
- * to the corrspeonding handler code.
+ * to the corresponding handler code.
* See ActionFactory for the syntax. Core defaults are in ActionFactory::CORE_ACTIONS,
* anything here overrides that.
*/
@@ -12472,7 +12472,7 @@ class MainConfigSchema {
/**
* Number of jobs to perform per request. May be less than one in which case jobs are
- * performed probabalistically. If this is zero, jobs will not be done during ordinary
+ * performed probabilistically. If this is zero, jobs will not be done during ordinary
* apache requests. In this case, maintenance/runJobs.php should be run in loop every
* few seconds via a service or cron job. If using a cron job, be sure to handle the
* case where the script is already running (e.g. via `/usr/bin/flock -n <lock_file>`).
@@ -12588,15 +12588,15 @@ class MainConfigSchema {
*
* By setting up a PubSub system (like Kafka) and enabling a corresponding EventRelayer class
* that uses it, MediaWiki can broadcast events to all subscribers. Certain features like WAN
- * cache purging and CDN cache purging will emit events to this system. Appropriate listers can
- * subscribe to the channel and take actions based on the events. For example, a local daemon
+ * cache purging and CDN cache purging will emit events to this system. Appropriate listeners
+ * can subscribe to the channel and take actions based on the events. For example, a local daemon
* can run on each CDN cache node and perform local purges based on the URL purge channel
* events.
*
* Some extensions may want to use "channel categories" so that different channels can also
* share the same custom relayer instance (e.g. when it's likely to be overridden). They can use
* EventRelayerGroup::getRelayer() based on the category but call notify() on various different
- * actual channels. One reason for this would be that some system have very different
+ * actual channels. One reason for this would be that some systems have very different
* performance vs durability needs, so one system (e.g. Kafka) may not be suitable for all
* uses.
*
diff --git a/includes/MediaWikiServices.php b/includes/MediaWikiServices.php
index 52814962efb4..22c5048faf12 100644
--- a/includes/MediaWikiServices.php
+++ b/includes/MediaWikiServices.php
@@ -27,13 +27,10 @@ use ExtensionRegistry;
use ExternalStoreAccess;
use ExternalStoreFactory;
use FileBackendGroup;
-use GenderCache;
-use HtmlCacheUpdater;
use IBufferingStatsdDataFactory;
use JobQueueGroup;
use JobRunner;
use Language;
-use LinkCache;
use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
use LocalisationCache;
use LogicException;
@@ -55,7 +52,11 @@ use MediaWiki\Block\DatabaseBlockStoreFactory;
use MediaWiki\Block\HideUserUtils;
use MediaWiki\Block\UnblockUserFactory;
use MediaWiki\Cache\BacklinkCacheFactory;
+use MediaWiki\Cache\GenderCache;
+use MediaWiki\Cache\HTMLCacheUpdater;
use MediaWiki\Cache\LinkBatchFactory;
+use MediaWiki\Cache\LinkCache;
+use MediaWiki\Cache\UserCache;
use MediaWiki\Category\TrackingCategories;
use MediaWiki\ChangeTags\ChangeTagsStore;
use MediaWiki\Collation\CollationFactory;
@@ -198,7 +199,6 @@ use SearchEngineConfig;
use SearchEngineFactory;
use SkinFactory;
use UploadRevisionImporter;
-use UserCache;
use WANObjectCache;
use WatchedItemQueryService;
use WatchedItemStoreInterface;
@@ -1192,7 +1192,7 @@ class MediaWikiServices extends ServiceContainer {
/**
* @since 1.35
*/
- public function getHtmlCacheUpdater(): HtmlCacheUpdater {
+ public function getHtmlCacheUpdater(): HTMLCacheUpdater {
return $this->getService( 'HtmlCacheUpdater' );
}
diff --git a/includes/Output/OutputPage.php b/includes/Output/OutputPage.php
index c43267109d9b..db597df73b8b 100644
--- a/includes/Output/OutputPage.php
+++ b/includes/Output/OutputPage.php
@@ -33,7 +33,7 @@ use InvalidArgumentException;
use JavaScriptContent;
use Language;
use LanguageCode;
-use LinkCache;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Config\Config;
use MediaWiki\Context\ContextSource;
use MediaWiki\Context\IContextSource;
diff --git a/includes/Permissions/PermissionManager.php b/includes/Permissions/PermissionManager.php
index e830a4ba2efc..e518c5274d19 100644
--- a/includes/Permissions/PermissionManager.php
+++ b/includes/Permissions/PermissionManager.php
@@ -26,6 +26,7 @@ use MediaWiki\Block\AbstractBlock;
use MediaWiki\Block\Block;
use MediaWiki\Block\BlockErrorFormatter;
use MediaWiki\Block\BlockManager;
+use MediaWiki\Cache\UserCache;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Context\IContextSource;
use MediaWiki\Context\RequestContext;
@@ -53,7 +54,6 @@ use MediaWiki\User\UserIdentity;
use MessageSpecifier;
use PermissionsError;
use StatusValue;
-use UserCache;
use Wikimedia\ScopedCallback;
/**
diff --git a/includes/Permissions/RestrictionStore.php b/includes/Permissions/RestrictionStore.php
index ef59784cb474..69439a50772e 100644
--- a/includes/Permissions/RestrictionStore.php
+++ b/includes/Permissions/RestrictionStore.php
@@ -4,8 +4,8 @@ namespace MediaWiki\Permissions;
use DBAccessObjectUtils;
use IDBAccessObject;
-use LinkCache;
use MediaWiki\Cache\CacheKeyHelper;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\CommentStore\CommentStore;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\HookContainer\HookContainer;
diff --git a/includes/ResourceLoader/CodexModule.php b/includes/ResourceLoader/CodexModule.php
index b9935f581c84..3f72f5241514 100644
--- a/includes/ResourceLoader/CodexModule.php
+++ b/includes/ResourceLoader/CodexModule.php
@@ -192,6 +192,15 @@ class CodexModule extends FileModule {
return false;
}
+ public function supportsURLLoading() {
+ // We need to override this explicitly. The parent method might return true if there are
+ // no 'packageFiles' set in the module definition and they're all generated by us.
+ // It's possible that this "should" return true in some circumstances (e.g. style-only use
+ // of CodexModule combined with non-packageFiles scripts), but those are edge cases that
+ // we're choosing not to support here.
+ return false;
+ }
+
/**
* Get the theme to use based on the current skin.
*
diff --git a/includes/ResourceLoader/UserModule.php b/includes/ResourceLoader/UserModule.php
index b5faa8f6b6ab..1bf3159f3f19 100644
--- a/includes/ResourceLoader/UserModule.php
+++ b/includes/ResourceLoader/UserModule.php
@@ -41,7 +41,8 @@ class UserModule extends WikiModule {
*/
protected function getPages( Context $context ) {
$user = $context->getUserIdentity();
- if ( !$user || !$user->isRegistered() ) {
+ $tempUserConfig = MediaWikiServices::getInstance()->getTempUserConfig();
+ if ( !$user || !$user->isRegistered() || $tempUserConfig->isTempName( $user->getName() ) ) {
return [];
}
diff --git a/includes/ResourceLoader/UserStylesModule.php b/includes/ResourceLoader/UserStylesModule.php
index 3721da831b9b..ebd6dfa02258 100644
--- a/includes/ResourceLoader/UserStylesModule.php
+++ b/includes/ResourceLoader/UserStylesModule.php
@@ -42,7 +42,8 @@ class UserStylesModule extends WikiModule {
*/
protected function getPages( Context $context ) {
$user = $context->getUserIdentity();
- if ( !$user || !$user->isRegistered() ) {
+ $tempUserConfig = MediaWikiServices::getInstance()->getTempUserConfig();
+ if ( !$user || !$user->isRegistered() || $tempUserConfig->isTempName( $user->getName() ) ) {
return [];
}
diff --git a/includes/Rest/Handler.php b/includes/Rest/Handler.php
index 46c49590b7f6..8ec37de4be84 100644
--- a/includes/Rest/Handler.php
+++ b/includes/Rest/Handler.php
@@ -539,7 +539,10 @@ abstract class Handler {
}
return $parsedBody;
default:
- return null;
+ throw new LocalizedHttpException(
+ new MessageValue( 'rest-unsupported-content-type', [ $contentType ?? '(null)' ] ),
+ 415
+ );
}
}
diff --git a/includes/Rest/Handler/Helper/HtmlInputTransformHelper.php b/includes/Rest/Handler/Helper/HtmlInputTransformHelper.php
index 00cff0a96797..c93ff49a49df 100644
--- a/includes/Rest/Handler/Helper/HtmlInputTransformHelper.php
+++ b/includes/Rest/Handler/Helper/HtmlInputTransformHelper.php
@@ -527,7 +527,7 @@ class HtmlInputTransformHelper {
return $this->transform->htmlToContent();
} catch ( ClientError $e ) {
throw new LocalizedHttpException(
- new MessageValue( 'rest-html-backend-error' ),
+ new MessageValue( 'rest-html-backend-error', [ $e->getMessage() ] ),
400,
[ 'reason' => $e->getMessage() ]
);
diff --git a/includes/Rest/Handler/Helper/HtmlOutputHelper.php b/includes/Rest/Handler/Helper/HtmlOutputHelper.php
index fe8b522d4cc3..dc7996d37ff5 100644
--- a/includes/Rest/Handler/Helper/HtmlOutputHelper.php
+++ b/includes/Rest/Handler/Helper/HtmlOutputHelper.php
@@ -52,6 +52,7 @@ interface HtmlOutputHelper {
* @see Handler::getETag()
*
* @param string $suffix A suffix to attach to the etag.
+ * Must consist of characters that are legal in ETags.
*
* @return string|null We return null when there is no etag.
*/
@@ -78,6 +79,12 @@ interface HtmlOutputHelper {
/**
* Set the language to be used for variant conversion.
*
+ * If $targetLanguage is a string, it may be a list of language ranges as
+ * specified by RFC 9110 for use in the Accept-Language header.
+ * Implementations must be able to process this format, and may use the
+ * information provided to choose a supported target language that is
+ * desirable to the client.
+ *
* @param Bcp47Code|string $targetLanguage
* @param Bcp47Code|string|null $sourceLanguage
*/
diff --git a/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php b/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php
index 20cdc2aa8b9a..f312e8aa7206 100644
--- a/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php
+++ b/includes/Rest/Handler/Helper/HtmlOutputRendererHelper.php
@@ -30,6 +30,7 @@ use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Edit\ParsoidOutputStash;
use MediaWiki\Edit\SelserContext;
use MediaWiki\Languages\LanguageFactory;
+use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MainConfigNames;
use MediaWiki\Page\PageIdentity;
use MediaWiki\Page\ParserOutputAccess;
@@ -168,7 +169,7 @@ class HtmlOutputRendererHelper implements HtmlOutputHelper {
* @param bool $lenientRevHandling Should we ignore mismatches
* $page and the page that $revision belongs to? Usually happens
* because of page moves. This should be set to true only for
- * internal API calls.
+ * internal API calls.
*/
public function __construct(
ParsoidOutputStash $parsoidOutputStash,
@@ -409,6 +410,7 @@ class HtmlOutputRendererHelper implements HtmlOutputHelper {
$sourceLanguage = null
): void {
if ( is_string( $targetLanguage ) ) {
+ $targetLanguage = $this->getAcceptedTargetLanguage( $targetLanguage );
$targetLanguage = LanguageCode::normalizeNonstandardCodeAndWarn(
$targetLanguage
);
@@ -423,6 +425,22 @@ class HtmlOutputRendererHelper implements HtmlOutputHelper {
}
/**
+ * Get a target language from an accept header
+ */
+ private function getAcceptedTargetLanguage( string $targetLanguage ): string {
+ // We could try to identify the most desirable language here,
+ // following the rules for Accept-Language headers in RFC9100.
+ // For now, just take the first language code.
+
+ if ( preg_match( '/^\s*([-\w]+)/', $targetLanguage, $m ) ) {
+ return $m[1];
+ } else {
+ // "undetermined" per RFC5646
+ return 'und';
+ }
+ }
+
+ /**
* @inheritDoc
*/
public function getHtml(): ParserOutput {
@@ -447,10 +465,16 @@ class HtmlOutputRendererHelper implements HtmlOutputHelper {
);
if ( !$stashSuccess ) {
$this->stats->increment( 'htmloutputrendererhelper.stash.fail' );
+
+ $errorData = [ 'parsoid-stash-key' => $parsoidStashKey ];
+ LoggerFactory::getInstance( 'HtmlOutputRendererHelper' )->error(
+ "Parsoid stash failure",
+ $errorData
+ );
throw new LocalizedHttpException(
- MessageValue::new( 'rest-html-backend-error' ),
+ MessageValue::new( 'rest-html-stash-failure' ),
500,
- [ 'reason' => 'Failed to stash parser output' ]
+ $errorData
);
}
$this->stats->increment( 'htmloutputrendererhelper.stash.save' );
@@ -572,7 +596,10 @@ class HtmlOutputRendererHelper implements HtmlOutputHelper {
if ( !$status->isOK() ) {
if ( $status->hasMessage( 'parsoid-client-error' ) ) {
throw new LocalizedHttpException(
- MessageValue::new( 'rest-html-backend-error' ),
+ MessageValue::new(
+ 'rest-html-backend-error',
+ [ $this->getStatusAsString( $status ) ]
+ ),
400,
[ 'reason' => $status->getErrors() ]
);
@@ -583,10 +610,19 @@ class HtmlOutputRendererHelper implements HtmlOutputHelper {
[ 'reason' => $status->getErrors() ]
);
} else {
+ $errorData = [ 'reason' => $status->getErrors() ];
+ LoggerFactory::getInstance( 'HtmlOutputRendererHelper' )->error(
+ "Parsoid backend error",
+ $errorData
+ );
+
throw new LocalizedHttpException(
- MessageValue::new( 'rest-html-backend-error' ),
+ MessageValue::new(
+ 'rest-html-backend-error',
+ [ $this->getStatusAsString( $status ) ]
+ ),
500,
- [ 'reason' => $status->getErrors() ]
+ $errorData
);
}
}
@@ -745,6 +781,11 @@ class HtmlOutputRendererHelper implements HtmlOutputHelper {
if ( $this->flavor === 'fragment' || $this->getRevisionId() === null ) {
$this->isCacheable = false;
}
+
+ // TODO: Decide whether we want to allow stale content for speed for the
+ // 'view' flavor. In that case, we would want to use PoolCounterWork,
+ // either directly or through ParserOutputAccess.
+
if ( $this->isCacheable ) {
$flags = $this->parsoidOutputAccessOptions;
$status = $this->parsoidOutputAccess->getParserOutput(
@@ -801,4 +842,14 @@ class HtmlOutputRendererHelper implements HtmlOutputHelper {
return $status;
}
+ private function getStatusAsString( Status $status ): string {
+ // Ideally, we should be able to just use a Status object as a
+ // message parameter. Until we can do that, we are stuck with the
+ // deprecated method. The alternative would be to inject a
+ // StatusFormatter, but StatusFormatter depends on request data for
+ // the localization context, so it cannot be injected through service
+ // wiring.
+ return $status->getWikiText();
+ }
+
}
diff --git a/includes/Rest/Handler/ParsoidHandler.php b/includes/Rest/Handler/ParsoidHandler.php
index d8e90fb1171b..0bed5f7c6601 100644
--- a/includes/Rest/Handler/ParsoidHandler.php
+++ b/includes/Rest/Handler/ParsoidHandler.php
@@ -254,10 +254,13 @@ abstract class ParsoidHandler extends Handler {
}
}
- $acceptLanguage = $request->getHeaderLine( 'Accept-Language' ) ?: null;
- if ( $acceptLanguage ) {
+ // For use in getHtmlOutputRendererHelper
+ $opts['accept-language'] = $request->getHeaderLine( 'Accept-Language' ) ?: null;
+
+ $acceptLanguage = null;
+ if ( $opts['accept-language'] !== null ) {
$acceptLanguage = LanguageCode::normalizeNonstandardCodeAndWarn(
- $acceptLanguage
+ $opts['accept-language']
);
}
@@ -372,8 +375,8 @@ abstract class ParsoidHandler extends Handler {
$helper->setPageLanguage( $attribs['pagelanguage'] );
}
- if ( isset( $attribs['envOptions']['htmlVariantLanguage'] ) ) {
- $helper->setVariantConversionLanguage( $attribs['envOptions']['htmlVariantLanguage'] );
+ if ( isset( $attribs['opts']['accept-language'] ) ) {
+ $helper->setVariantConversionLanguage( $attribs['opts']['accept-language'] );
}
return $helper;
@@ -644,19 +647,6 @@ abstract class ParsoidHandler extends Handler {
}
}
- private function allowParserCacheWrite() {
- $config = RequestContext::getMain()->getConfig();
-
- // HACK: remove before the release of MW 1.40 / early 2023.
- if ( $config->has( 'TemporaryParsoidHandlerParserCacheWriteRatio' ) ) {
- // We need to be careful about ramping up the cache writes,
- // so we don't run out of disk space.
- return wfRandom() < $config->get( 'TemporaryParsoidHandlerParserCacheWriteRatio' );
- }
-
- return true;
- }
-
/**
* Wikitext -> HTML helper.
* Spec'd in https://phabricator.wikimedia.org/T75955 and the API tests.
@@ -714,13 +704,6 @@ abstract class ParsoidHandler extends Handler {
$pageConfig->getRevisionId() ?: null
);
- if ( !$this->allowParserCacheWrite() ) {
- // NOTE: In theory, we want to always write to the parser cache. However,
- // the ParserCache takes a lot of disk space, and we need to have fine grained control
- // over when we write to it, so we can avoid running out of disc space.
- $helper->setUseParserCache( true, false );
- }
-
$needsPageBundle = ( $format === ParsoidFormatHelper::FORMAT_PAGEBUNDLE );
if ( $attribs['body_only'] ) {
diff --git a/includes/Rest/RequestBase.php b/includes/Rest/RequestBase.php
index 7445e711bdb8..62d66450ad62 100644
--- a/includes/Rest/RequestBase.php
+++ b/includes/Rest/RequestBase.php
@@ -128,4 +128,34 @@ abstract class RequestBase implements RequestInterface {
}
return $ct;
}
+
+ /**
+ * Return true if the client provided a content-length header or a
+ * transfer-encoding header.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9110.html#name-content-length
+ *
+ * @return bool
+ */
+ public function hasBody(): bool {
+ // From RFC9110, section 8.6: A user agent SHOULD send Content-Length
+ // in a request when the method defines a meaning for enclosed content
+ // and it is not sending Transfer-Encoding. [...]
+ // A user agent SHOULD NOT send a Content-Length header field when the
+ // request message does not contain content and the method semantics do
+ // not anticipate such data.
+
+ if ( $this->getHeaderLine( 'content-length' ) !== '' ) {
+ // If a content length is set, there is a body
+ return true;
+ }
+
+ if ( $this->getHeaderLine( 'transfer-encoding' ) !== '' ) {
+ // If a transfer encoding is set, there is a body
+ return true;
+ }
+
+ return false;
+ }
+
}
diff --git a/includes/Rest/RequestData.php b/includes/Rest/RequestData.php
index 59469865ef5b..4e8026eee817 100644
--- a/includes/Rest/RequestData.php
+++ b/includes/Rest/RequestData.php
@@ -102,4 +102,21 @@ class RequestData extends RequestBase {
public function getPostParams() {
return $this->postParams;
}
+
+ public function hasBody(): bool {
+ if ( parent::hasBody() ) {
+ return true;
+ }
+
+ if ( $this->parsedBody !== null ) {
+ return true;
+ }
+
+ if ( $this->getBody()->getContents() !== '' ) {
+ return true;
+ }
+
+ return false;
+ }
+
}
diff --git a/includes/Rest/RequestInterface.php b/includes/Rest/RequestInterface.php
index 596da4eebe97..a813b3c7d160 100644
--- a/includes/Rest/RequestInterface.php
+++ b/includes/Rest/RequestInterface.php
@@ -268,4 +268,12 @@ interface RequestInterface {
public function setParsedBody( ?array $data );
public function getBodyType(): ?string;
+
+ /**
+ * Determines whether the request has body data associated with it.
+ * Note that this method may return true even if the body is empty.
+ *
+ * @return bool
+ */
+ public function hasBody(): bool;
}
diff --git a/includes/Rest/i18n/de.json b/includes/Rest/i18n/de.json
index ef99b6774fc1..6808d3ea94e5 100644
--- a/includes/Rest/i18n/de.json
+++ b/includes/Rest/i18n/de.json
@@ -37,7 +37,8 @@
"rest-page-source-type-error": "Der Inhaltstyp der angeforderten Seitenquelle wird nicht unterstützt",
"rest-no-revision": "Version für Titel $1 kann nicht abgerufen werden",
"rest-media-too-many-links": "Zu viele Medienlinks auf Titel $1 gefunden ($2 erlaubt)",
- "rest-html-backend-error": "Parsoid-HTML kann nicht abgerufen werden",
+ "rest-html-backend-error": "Parsoid-HTML kann nicht abgerufen werden: $1",
+ "rest-html-stash-failure": "Parsoid-HTML kann nicht gespeichert werden.",
"rest-bad-json-body": "Schlechter Hauptteil der Anfrage, muss ein JSON-Objekt sein.",
"rest-json-body-parse-error": "Parsen des Hauptteils der Anfrage als JSON fehlgeschlagen: $1",
"rest-missing-body-field": "Pflichtfeld „$1“ fehlt im Hauptteil der Anfrage.",
diff --git a/includes/Rest/i18n/en.json b/includes/Rest/i18n/en.json
index 4108092919f3..afda9f154d5f 100644
--- a/includes/Rest/i18n/en.json
+++ b/includes/Rest/i18n/en.json
@@ -37,7 +37,8 @@
"rest-page-source-type-error": "The content type of the page source requested is unsupported",
"rest-no-revision": "Unable to retrieve revision for title $1",
"rest-media-too-many-links": "Too many media links found on title $1 ($2 allowed)",
- "rest-html-backend-error": "Unable to fetch Parsoid HTML",
+ "rest-html-backend-error": "Unable to fetch Parsoid HTML: $1",
+ "rest-html-stash-failure": "Unable to stash Parsoid HTML.",
"rest-bad-json-body": "Bad request body, must be a JSON object.",
"rest-json-body-parse-error": "Parsing request body as JSON failed: $1",
"rest-missing-body-field": "Mandatory field \"$1\" missing from request body.",
diff --git a/includes/Rest/i18n/he.json b/includes/Rest/i18n/he.json
index 477e59cfaf4d..e60f22a887a0 100644
--- a/includes/Rest/i18n/he.json
+++ b/includes/Rest/i18n/he.json
@@ -38,7 +38,8 @@
"rest-page-source-type-error": "סוג התוכן של מקור הדף שהתבקש אינו נתמך",
"rest-no-revision": "לא ניתן לאחזר גרסה עבור הכותרת $1",
"rest-media-too-many-links": "נמצאו יותר מדי קישורי מדיה בכותרת $1 (המספר המרבי הוא $2)",
- "rest-html-backend-error": "לא ניתן לאחזר HTML של פרסואיד",
+ "rest-html-backend-error": "לא ניתן לאחזר HTML של פרסואיד: $1",
+ "rest-html-stash-failure": "לא ניתן לשמור Parsoid HTML במאגר (stash).",
"rest-bad-json-body": "גוף בקשה גרוע, חייב להיות עצם JSON.",
"rest-json-body-parse-error": "פענוח גוף הבקשה בתור JSON נכשל: $1",
"rest-missing-body-field": "שדה החובה \"$1\" חסר מגוף הבקשה.",
diff --git a/includes/Rest/i18n/qqq.json b/includes/Rest/i18n/qqq.json
index 30e606ee4e9e..b6867630ed59 100644
--- a/includes/Rest/i18n/qqq.json
+++ b/includes/Rest/i18n/qqq.json
@@ -41,7 +41,8 @@
"rest-page-source-type-error": "Error message for REST API debugging, shown when trying to retrieve content for a page that has an unsupported content type",
"rest-no-revision": "Error message for REST API debugging, shown when fetching a revision by page ID fails. Parameters:\n* $1: The page ID we are getting revision from",
"rest-media-too-many-links": "Error message for REST API debugging, shown when there are too many media links on a page. Parameters:\n* $1: The page title.\n* $2: The number of links allowed.",
- "rest-html-backend-error": "Error message for REST API debugging, shown when fetching Parsoid HTML from backend has failed.",
+ "rest-html-backend-error": "Error message for REST API debugging, shown when fetching Parsoid HTML from backend has failed. Parameters:\n* $1: The technical error message from parsoid.",
+ "rest-html-stash-failure": "Error message for REST API debugging, shown when stash Parsoid HTML failed.",
"rest-bad-json-body": "Error message for REST API debugging, shown when request body did not contain a JSON encoded object.",
"rest-json-body-parse-error": "Error message for REST API debugging, shown when parsing the JSON body failed. Parameters:\n* $1: the error message from the JSON parser.",
"rest-missing-body-field": "Error message for REST API debugging, shown when there is a mandatory field missing from the request body. Parameters:\n* $1: The field name",
diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php
index b01ca9d5d351..f376305aeeb8 100644
--- a/includes/ServiceWiring.php
+++ b/includes/ServiceWiring.php
@@ -61,8 +61,13 @@ use MediaWiki\Block\DatabaseBlockStoreFactory;
use MediaWiki\Block\HideUserUtils;
use MediaWiki\Block\UnblockUserFactory;
use MediaWiki\Block\UserBlockCommandFactory;
+use MediaWiki\Cache\BacklinkCache;
use MediaWiki\Cache\BacklinkCacheFactory;
+use MediaWiki\Cache\GenderCache;
+use MediaWiki\Cache\HTMLCacheUpdater;
use MediaWiki\Cache\LinkBatchFactory;
+use MediaWiki\Cache\LinkCache;
+use MediaWiki\Cache\UserCache;
use MediaWiki\Category\TrackingCategories;
use MediaWiki\ChangeTags\ChangeTagsStore;
use MediaWiki\Collation\CollationFactory;
@@ -938,10 +943,10 @@ return [
return $hookContainer;
},
- 'HtmlCacheUpdater' => static function ( MediaWikiServices $services ): HtmlCacheUpdater {
+ 'HtmlCacheUpdater' => static function ( MediaWikiServices $services ): HTMLCacheUpdater {
$config = $services->getMainConfig();
- return new HtmlCacheUpdater(
+ return new HTMLCacheUpdater(
$services->getHookContainer(),
$services->getTitleFactory(),
$config->get( MainConfigNames::CdnReboundPurgeDelay ),
diff --git a/includes/actions/RawAction.php b/includes/actions/RawAction.php
index 5fafbb35bc95..cb63d7335e94 100644
--- a/includes/actions/RawAction.php
+++ b/includes/actions/RawAction.php
@@ -116,7 +116,7 @@ class RawAction extends FormlessAction {
$contentType == 'text/javascript'
) {
// CSS/JSON/JS raw content has its own CDN max age configuration.
- // Note: HtmlCacheUpdater::getUrls() includes action=raw for css/json/js
+ // Note: HTMLCacheUpdater::getUrls() includes action=raw for css/json/js
// pages, so if using the canonical url, this will get HTCP purges.
$smaxage = intval( $config->get( MainConfigNames::ForcedRawSMaxage ) );
} else {
diff --git a/includes/api/ApiPageSet.php b/includes/api/ApiPageSet.php
index e3dbe8fff79d..c38ae9f66bcc 100644
--- a/includes/api/ApiPageSet.php
+++ b/includes/api/ApiPageSet.php
@@ -21,7 +21,10 @@
*/
use MediaWiki\Api\Validator\SubmoduleDef;
+use MediaWiki\Cache\GenderCache;
+use MediaWiki\Cache\LinkBatch;
use MediaWiki\Cache\LinkBatchFactory;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MainConfigNames;
use MediaWiki\MediaWikiServices;
diff --git a/includes/api/ApiParse.php b/includes/api/ApiParse.php
index a7c686686c27..aee342be3245 100644
--- a/includes/api/ApiParse.php
+++ b/includes/api/ApiParse.php
@@ -21,6 +21,7 @@
*/
use MediaWiki\Cache\LinkBatchFactory;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\CommentFormatter\CommentFormatter;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Content\Renderer\ContentRenderer;
diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php
index 95c29a553bc0..e8e062bc043a 100644
--- a/includes/api/ApiQuery.php
+++ b/includes/api/ApiQuery.php
@@ -59,6 +59,7 @@ class ApiQuery extends ApiBase {
'ActorMigration',
'UserGroupManager',
'GroupPermissionsLookup',
+ 'TempUserConfig'
]
],
'deletedrevisions' => [
diff --git a/includes/api/ApiQueryAllLinks.php b/includes/api/ApiQueryAllLinks.php
index b0a6c160accf..884bee3ed221 100644
--- a/includes/api/ApiQueryAllLinks.php
+++ b/includes/api/ApiQueryAllLinks.php
@@ -20,6 +20,7 @@
* @file
*/
+use MediaWiki\Cache\GenderCache;
use MediaWiki\Linker\LinksMigration;
use MediaWiki\ParamValidator\TypeDef\NamespaceDef;
use MediaWiki\Title\NamespaceInfo;
diff --git a/includes/api/ApiQueryAllPages.php b/includes/api/ApiQueryAllPages.php
index f410766e06ad..9613df8ef093 100644
--- a/includes/api/ApiQueryAllPages.php
+++ b/includes/api/ApiQueryAllPages.php
@@ -20,6 +20,7 @@
* @file
*/
+use MediaWiki\Cache\GenderCache;
use MediaWiki\MainConfigNames;
use MediaWiki\Permissions\RestrictionStore;
use MediaWiki\Title\NamespaceInfo;
diff --git a/includes/api/ApiQueryContributors.php b/includes/api/ApiQueryContributors.php
index d66f004e5154..4d802d59b366 100644
--- a/includes/api/ApiQueryContributors.php
+++ b/includes/api/ApiQueryContributors.php
@@ -28,6 +28,7 @@ use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Revision\RevisionStore;
use MediaWiki\Title\Title;
use MediaWiki\User\ActorMigration;
+use MediaWiki\User\TempUser\TempUserConfig;
use MediaWiki\User\UserGroupManager;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef\IntegerDef;
@@ -49,6 +50,7 @@ class ApiQueryContributors extends ApiQueryBase {
private ActorMigration $actorMigration;
private UserGroupManager $userGroupManager;
private GroupPermissionsLookup $groupPermissionsLookup;
+ private TempUserConfig $tempUserConfig;
/**
* @param ApiQuery $query
@@ -57,6 +59,7 @@ class ApiQueryContributors extends ApiQueryBase {
* @param ActorMigration $actorMigration
* @param UserGroupManager $userGroupManager
* @param GroupPermissionsLookup $groupPermissionsLookup
+ * @param TempUserConfig $tempUserConfig
*/
public function __construct(
ApiQuery $query,
@@ -64,7 +67,8 @@ class ApiQueryContributors extends ApiQueryBase {
RevisionStore $revisionStore,
ActorMigration $actorMigration,
UserGroupManager $userGroupManager,
- GroupPermissionsLookup $groupPermissionsLookup
+ GroupPermissionsLookup $groupPermissionsLookup,
+ TempUserConfig $tempUserConfig
) {
// "pc" is short for "page contributors", "co" was already taken by the
// GeoData extension's prop=coordinates.
@@ -73,6 +77,7 @@ class ApiQueryContributors extends ApiQueryBase {
$this->actorMigration = $actorMigration;
$this->userGroupManager = $userGroupManager;
$this->groupPermissionsLookup = $groupPermissionsLookup;
+ $this->tempUserConfig = $tempUserConfig;
}
public function execute() {
@@ -300,4 +305,11 @@ class ApiQueryContributors extends ApiQueryBase {
public function getHelpUrls() {
return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Contributors';
}
+
+ protected function getSummaryMessage() {
+ if ( $this->tempUserConfig->isEnabled() ) {
+ return 'apihelp-query+contributors-summary-tempusers-enabled';
+ }
+ return parent::getSummaryMessage();
+ }
}
diff --git a/includes/api/ApiQueryUserInfo.php b/includes/api/ApiQueryUserInfo.php
index 1a5b53cbcc15..ab43b4daa48c 100644
--- a/includes/api/ApiQueryUserInfo.php
+++ b/includes/api/ApiQueryUserInfo.php
@@ -149,6 +149,10 @@ class ApiQueryUserInfo extends ApiQueryBase {
$vals['anon'] = true;
}
+ if ( $user->isTemp() ) {
+ $vals['temp'] = true;
+ }
+
if ( isset( $this->prop['blockinfo'] ) ) {
$block = $user->getBlock();
if ( $block ) {
diff --git a/includes/api/ApiQueryUsers.php b/includes/api/ApiQueryUsers.php
index 0825a86bd880..f0707d42a735 100644
--- a/includes/api/ApiQueryUsers.php
+++ b/includes/api/ApiQueryUsers.php
@@ -21,6 +21,7 @@
*/
use MediaWiki\Auth\AuthManager;
+use MediaWiki\Cache\GenderCache;
use MediaWiki\User\User;
use MediaWiki\User\UserFactory;
use MediaWiki\User\UserGroupManager;
diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php
index 26f0c53559b5..78c545d51b20 100644
--- a/includes/api/ApiQueryWatchlist.php
+++ b/includes/api/ApiQueryWatchlist.php
@@ -20,6 +20,7 @@
* @file
*/
+use MediaWiki\Cache\GenderCache;
use MediaWiki\CommentFormatter\CommentFormatter;
use MediaWiki\CommentStore\CommentStore;
use MediaWiki\Linker\LinkTarget;
diff --git a/includes/api/ApiQueryWatchlistRaw.php b/includes/api/ApiQueryWatchlistRaw.php
index 43fcdfcc3c79..769e49e04d41 100644
--- a/includes/api/ApiQueryWatchlistRaw.php
+++ b/includes/api/ApiQueryWatchlistRaw.php
@@ -20,6 +20,7 @@
* @file
*/
+use MediaWiki\Cache\GenderCache;
use MediaWiki\ParamValidator\TypeDef\UserDef;
use MediaWiki\Title\NamespaceInfo;
use MediaWiki\Title\Title;
diff --git a/includes/api/ApiUpload.php b/includes/api/ApiUpload.php
index 4b420bf46de8..5bd20a1cc432 100644
--- a/includes/api/ApiUpload.php
+++ b/includes/api/ApiUpload.php
@@ -969,7 +969,7 @@ class ApiUpload extends ApiBase {
$this->log->info( "Upload stashing of {filename} failed for {user} because {error}",
[
'user' => $this->getUser()->getName(),
- 'status' => get_class( $e ),
+ 'error' => get_class( $e ),
'filename' => $this->mParams['filename'] ?? '-',
'filekey' => $this->mParams['filekey'] ?? '-'
]
diff --git a/includes/api/i18n/de.json b/includes/api/i18n/de.json
index b1a17d32fc5a..f3d07a3d81e1 100644
--- a/includes/api/i18n/de.json
+++ b/includes/api/i18n/de.json
@@ -707,7 +707,8 @@
"apihelp-query+categorymembers-param-endsortkey": "Stattdessen $1endhexsortkey verwenden.",
"apihelp-query+categorymembers-example-simple": "Rufe die ersten 10 Seiten von <kbd>Category:Physics</kbd> ab.",
"apihelp-query+categorymembers-example-generator": "Rufe die Seiteninformationen zu den ersten 10 Seiten von<kbd>Category:Physics</kbd> ab.",
- "apihelp-query+contributors-summary": "Rufe die Liste der angemeldeten Bearbeiter und die Zahl anonymer Bearbeiter einer Seite ab.",
+ "apihelp-query+contributors-summary": "Rufe die Liste der angemeldeten Bearbeiter und die Zahl nicht angemeldeter Bearbeiter einer Seite ab.",
+ "apihelp-query+contributors-summary-tempusers-enabled": "Abrufen der Liste der angemeldeten Benutzer (einschließlich temporärer Benutzer) und die Anzahl der abgemeldeten Benutzer einer Seite.",
"apihelp-query+contributors-param-group": "Nur Benutzer der angegebenen Gruppen einbeziehen. Beinhaltet keine impliziten oder automatisch zugeteilten Gruppen wie *, user oder autoconfirmed.",
"apihelp-query+contributors-param-excluderights": "Ausschließen von Benutzer mit den angegebenen Rechten. Beinhaltet keine Rechte, die von impliziten oder automatisch hochgestuften Gruppen wie *, Benutzer oder automatisch bestätigt wurden.",
"apihelp-query+contributors-param-limit": "Wie viele Spender zurückgegeben werden sollen.",
diff --git a/includes/api/i18n/en.json b/includes/api/i18n/en.json
index 0b28a2062600..64030463e462 100644
--- a/includes/api/i18n/en.json
+++ b/includes/api/i18n/en.json
@@ -767,7 +767,8 @@
"apihelp-query+categorymembers-example-simple": "Get first 10 pages in <kbd>Category:Physics</kbd>.",
"apihelp-query+categorymembers-example-generator": "Get page info about first 10 pages in <kbd>Category:Physics</kbd>.",
- "apihelp-query+contributors-summary": "Get the list of logged-in contributors and the count of anonymous contributors to a page.",
+ "apihelp-query+contributors-summary": "Get the list of logged-in contributors and the count of logged-out contributors to a page.",
+ "apihelp-query+contributors-summary-tempusers-enabled": "Get the list of logged-in contributors (including temporary users) and the count of logged-out contributors to a page.",
"apihelp-query+contributors-param-group": "Only include users in the given groups. Does not include implicit or auto-promoted groups like *, user, or autoconfirmed.",
"apihelp-query+contributors-param-excludegroup": "Exclude users in the given groups. Does not include implicit or auto-promoted groups like *, user, or autoconfirmed.",
"apihelp-query+contributors-param-rights": "Only include users having the given rights. Does not include rights granted by implicit or auto-promoted groups like *, user, or autoconfirmed.",
diff --git a/includes/api/i18n/he.json b/includes/api/i18n/he.json
index 39e696765968..430a8e23b3a5 100644
--- a/includes/api/i18n/he.json
+++ b/includes/api/i18n/he.json
@@ -727,7 +727,8 @@
"apihelp-query+categorymembers-param-endsortkey": "כדאי להשתמש ב־$1endhexsortkey במקום.",
"apihelp-query+categorymembers-example-simple": "קבלת עשרת הדפים הראשונים שתחת <kbd>Category:Physics</kbd>.",
"apihelp-query+categorymembers-example-generator": "קבל מידע על הדף עבור 10 הדפים הראשונים ב־<kbd>Category:Physics</kbd>.",
- "apihelp-query+contributors-summary": "קבלת רשימה של תורמים שנכנסו לחשבון ומניין של תורמים אלמוניים לדף.",
+ "apihelp-query+contributors-summary": "קבלת רשימה של תורמים שנכנסו לחשבון ומניין של תורמים שלא נכנסו לחשבון לדף.",
+ "apihelp-query+contributors-summary-tempusers-enabled": "קבלת רשימה של תורמים שנכנסו לחשבון (כולל משתמשים זמניים) ומניין של תורמים שלא נכנסו לחשבון לדף.",
"apihelp-query+contributors-param-group": "לכלול רק משתמשים בקבוצות הנתונות. לא כולל קבוצות משתמעות או אוטומטיות כגון *, user או autoconfirmed.",
"apihelp-query+contributors-param-excludegroup": "לא לכלול משתמשים בקבוצות הנתונות. לא כולל קבוצות משתמעות או אוטומטיות כגון *, user או autoconfirmed.",
"apihelp-query+contributors-param-rights": "לכלול רק משתמשים עם ההרשאות הנתונות. לא כולל הרשאות שניתנו בקבוצות משתמעות או אוטומטיות כגון *, user או autoconfirmed.",
diff --git a/includes/api/i18n/nb.json b/includes/api/i18n/nb.json
index 05e38695c919..aa143fb5bfc2 100644
--- a/includes/api/i18n/nb.json
+++ b/includes/api/i18n/nb.json
@@ -718,7 +718,8 @@
"apihelp-query+categorymembers-param-endsortkey": "Bruk $1endhexsortkey i stedet.",
"apihelp-query+categorymembers-example-simple": "Hent de første 10 sidene i <kbd>Category:Physics</kbd>.",
"apihelp-query+categorymembers-example-generator": "Hent sideinformasjon om de første 10 sidene i <kbd>Category:Physics</kbd>.",
- "apihelp-query+contributors-summary": "Hent listen over innloggede bidrag og antall anonyme bidrag til siden.",
+ "apihelp-query+contributors-summary": "Hent listen over innloggede bidragsytere og antall anonyme bidragsytere til siden.",
+ "apihelp-query+contributors-summary-tempusers-enabled": "Hent listen over innloggede bidragsytere (inkludert midlertidige brukere) og antall utloggede bidragsytere til en side.",
"apihelp-query+contributors-param-group": "Bare inkluder brukere i de gitte gruppene. Inkluderer ikke implisitte eller autoforfremmede grupper som *, user eller autoconfirmed.",
"apihelp-query+contributors-param-excludegroup": "Ekskluder brukere i de gitte gruppene. Inkluderer ikke implisitte eller autoforfremmede grupper som *, user eller autoconfirmed.",
"apihelp-query+contributors-param-rights": "Bare inkluder brukere som har de gitte rettighetene. Inkluderer ikke rettigheter som gis av implisitte eller autoforfremmede grupper som *, user eller autoconfirmed.",
diff --git a/includes/api/i18n/qqq.json b/includes/api/i18n/qqq.json
index 1caa933b2859..73129343442e 100644
--- a/includes/api/i18n/qqq.json
+++ b/includes/api/i18n/qqq.json
@@ -731,7 +731,8 @@
"apihelp-query+categorymembers-param-endsortkey": "{{doc-apihelp-param|query+categorymembers|endsortkey}}",
"apihelp-query+categorymembers-example-simple": "{{doc-apihelp-example|query+categorymembers}}",
"apihelp-query+categorymembers-example-generator": "{{doc-apihelp-example|query+categorymembers}}",
- "apihelp-query+contributors-summary": "{{doc-apihelp-summary|query+contributors}}",
+ "apihelp-query+contributors-summary": "{{doc-apihelp-summary|query+contributors|seealso={{msg-mw|apihelp-query+contributors-summary-tempusers-enabled}} for the summary message used when temporary accounts are enabled.}}",
+ "apihelp-query+contributors-summary-tempusers-enabled": "{{doc-apihelp-summary|query+contributors|seealso={{msg-mw|apihelp-query+contributors-summary}} for the summary message used when temporary accounts are not enabled.}}",
"apihelp-query+contributors-param-group": "{{doc-apihelp-param|query+contributors|group}}",
"apihelp-query+contributors-param-excludegroup": "{{doc-apihelp-param|query+contributors|excludegroup}}",
"apihelp-query+contributors-param-rights": "{{doc-apihelp-param|query+contributors|rights}}",
diff --git a/includes/block/DatabaseBlockStore.php b/includes/block/DatabaseBlockStore.php
index 8ad3e1d4701f..2f2c70816614 100644
--- a/includes/block/DatabaseBlockStore.php
+++ b/includes/block/DatabaseBlockStore.php
@@ -1792,10 +1792,21 @@ class DatabaseBlockStore {
'ipb_sitewide' => $block->isSitewide(),
];
+ if ( $block->getExpiry() !== 'infinity' ) {
+ // Shorten the autoblock expiry if the parent block expiry is sooner.
+ // Don't lengthen -- that is only done when the IP address is actually
+ // used by the blocked user.
+ $blockArray[] = 'ipb_expiry=' . $dbw->conditional(
+ $dbw->expr( 'ipb_expiry', '>', $dbw->timestamp( $block->getExpiry() ) ),
+ $dbw->addQuotes( $dbw->timestamp( $block->getExpiry() ) ),
+ 'ipb_expiry'
+ );
+ }
+
$commentArray = $this->commentStore->insert(
$dbw,
'ipb_reason',
- $block->getReasonComment()
+ $this->getAutoblockReason( $block )
);
} else {
$blockArray = [
@@ -1806,10 +1817,21 @@ class DatabaseBlockStore {
'bl_sitewide' => $block->isSitewide(),
];
+ // Shorten the autoblock expiry if the parent block expiry is sooner.
+ // Don't lengthen -- that is only done when the IP address is actually
+ // used by the blocked user.
+ if ( $block->getExpiry() !== 'infinity' ) {
+ $blockArray[] = 'bl_expiry=' . $dbw->conditional(
+ $dbw->expr( 'bl_expiry', '>', $dbw->timestamp( $block->getExpiry() ) ),
+ $dbw->addQuotes( $dbw->timestamp( $block->getExpiry() ) ),
+ 'bl_expiry'
+ );
+ }
+
$commentArray = $this->commentStore->insert(
$dbw,
'bl_reason',
- $block->getReasonComment()
+ $this->getAutoblockReason( $block )
);
}
@@ -1955,28 +1977,12 @@ class DatabaseBlockStore {
}
$timestamp = wfTimestampNow();
- if ( $parentBlock->getExpiry() == 'infinity' ) {
- // Original block was indefinite, start an autoblock now
- $expiry = $this->getAutoblockExpiry( $timestamp );
- } else {
- // If the user is already blocked with an expiry date, we don't
- // want to pile on top of that.
- $expiry = min(
- $parentBlock->getExpiry(),
- $this->getAutoblockExpiry( $timestamp )
- );
- }
-
+ $expiry = $this->getAutoblockExpiry( $timestamp, $parentBlock->getExpiry() );
$autoblock = new DatabaseBlock( [
'wiki' => $this->wikiId,
'address' => UserIdentityValue::newAnonymous( $target, $this->wikiId ),
'by' => $blocker,
- 'reason' =>
- wfMessage(
- 'autoblocker',
- $parentBlock->getTargetName(),
- $parentBlock->getReasonComment()->text
- )->inContentLanguage()->plain(),
+ 'reason' => $this->getAutoblockReason( $parentBlock ),
'decodedTimestamp' => $timestamp,
'auto' => true,
'createAccount' => $parentBlock->isCreateAccountBlocked(),
@@ -1997,6 +2003,14 @@ class DatabaseBlockStore {
: false;
}
+ private function getAutoblockReason( DatabaseBlock $parentBlock ) {
+ return wfMessage(
+ 'autoblocker',
+ $parentBlock->getTargetName(),
+ $parentBlock->getReasonComment()->text
+ )->inContentLanguage()->plain();
+ }
+
/**
* Update the timestamp on autoblocks.
*
@@ -2008,8 +2022,11 @@ class DatabaseBlockStore {
if ( $block->getType() !== Block::TYPE_AUTO ) {
return;
}
- $block->setTimestamp( wfTimestamp() );
- $block->setExpiry( $this->getAutoblockExpiry( $block->getTimestamp() ) );
+ $now = wfTimestamp();
+ $block->setTimestamp( $now );
+ // No need to reduce the autoblock expiry to the expiry of the parent
+ // block, since the caller already checked for that.
+ $block->setExpiry( $this->getAutoblockExpiry( $now ) );
$dbw = $this->getPrimaryDB();
if ( $this->writeStage & SCHEMA_COMPAT_WRITE_OLD ) {
@@ -2041,14 +2058,21 @@ class DatabaseBlockStore {
/**
* Get the expiry timestamp for an autoblock created at the given time.
*
+ * If the parent block expiry is specified, the return value will be earlier
+ * than or equal to the parent block expiry.
+ *
* @internal Public to support deprecated DatabaseBlock method
* @param string|int $timestamp
+ * @param string|null $parentExpiry
* @return string
*/
- public function getAutoblockExpiry( $timestamp ) {
- $autoblockExpiry = $this->options->get( MainConfigNames::AutoblockExpiry );
-
- return wfTimestamp( TS_MW, (int)wfTimestamp( TS_UNIX, $timestamp ) + $autoblockExpiry );
+ public function getAutoblockExpiry( $timestamp, string $parentExpiry = null ) {
+ $maxDuration = $this->options->get( MainConfigNames::AutoblockExpiry );
+ $expiry = wfTimestamp( TS_MW, (int)wfTimestamp( TS_UNIX, $timestamp ) + $maxDuration );
+ if ( $parentExpiry !== null && $parentExpiry !== 'infinity' ) {
+ $expiry = min( $parentExpiry, $expiry );
+ }
+ return $expiry;
}
// endregion -- end of database write methods
diff --git a/includes/cache/BacklinkCache.php b/includes/cache/BacklinkCache.php
index 754dd3db5573..d1f187bcddbc 100644
--- a/includes/cache/BacklinkCache.php
+++ b/includes/cache/BacklinkCache.php
@@ -25,7 +25,10 @@
* @copyright © 2011, Antoine Musso
*/
-use MediaWiki\Cache\CacheKeyHelper;
+namespace MediaWiki\Cache;
+
+use Iterator;
+use LogicException;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\HookContainer\HookRunner;
@@ -36,6 +39,9 @@ use MediaWiki\Page\PageIdentityValue;
use MediaWiki\Page\PageReference;
use MediaWiki\Title\Title;
use MediaWiki\Title\TitleValue;
+use RuntimeException;
+use stdClass;
+use WANObjectCache;
use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IConnectionProvider;
use Wikimedia\Rdbms\IReadableDatabase;
@@ -553,3 +559,6 @@ class BacklinkCache {
return array_values( $mergedRes );
}
}
+
+/** @deprecated since 1.42 */
+class_alias( BacklinkCache::class, 'BacklinkCache' );
diff --git a/includes/cache/FileCacheBase.php b/includes/cache/FileCacheBase.php
index 7eeda2157f9d..50d847d3cb0a 100644
--- a/includes/cache/FileCacheBase.php
+++ b/includes/cache/FileCacheBase.php
@@ -21,10 +21,14 @@
* @ingroup Cache
*/
+namespace MediaWiki\Cache;
+
+use BagOStuff;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\MainConfigNames;
use MediaWiki\MediaWikiServices;
use MediaWiki\Request\WebRequest;
+use ObjectCache;
use Wikimedia\AtEase\AtEase;
use Wikimedia\IPUtils;
@@ -287,3 +291,6 @@ abstract class FileCacheBase {
return $cache->makeKey( static::class, 'misses', $this->mType, $this->mKey );
}
}
+
+/** @deprecated since 1.42 */
+class_alias( FileCacheBase::class, 'FileCacheBase' );
diff --git a/includes/cache/GenderCache.php b/includes/cache/GenderCache.php
index b098cba1712b..2b4fa4caed93 100644
--- a/includes/cache/GenderCache.php
+++ b/includes/cache/GenderCache.php
@@ -19,6 +19,8 @@
* @author Niklas Laxström
*/
+namespace MediaWiki\Cache;
+
use MediaWiki\Context\RequestContext;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MediaWikiServices;
@@ -182,3 +184,6 @@ class GenderCache {
return strtr( $username, '_', ' ' );
}
}
+
+/** @deprecated since 1.42 */
+class_alias( GenderCache::class, 'GenderCache' );
diff --git a/includes/cache/HtmlCacheUpdater.php b/includes/cache/HTMLCacheUpdater.php
index ca28633c5260..01fdbdbe1d0a 100644
--- a/includes/cache/HtmlCacheUpdater.php
+++ b/includes/cache/HTMLCacheUpdater.php
@@ -18,6 +18,8 @@
* @file
*/
+namespace MediaWiki\Cache;
+
use MediaWiki\Deferred\CdnCacheUpdate;
use MediaWiki\Deferred\DeferredUpdates;
use MediaWiki\Deferred\HtmlFileCacheUpdate;
@@ -26,6 +28,7 @@ use MediaWiki\HookContainer\HookRunner;
use MediaWiki\Page\PageIdentity;
use MediaWiki\Page\PageReference;
use MediaWiki\Title\TitleFactory;
+use Traversable;
/**
* Class to invalidate the CDN and HTMLFileCache entries associated with URLs/titles
@@ -33,7 +36,7 @@ use MediaWiki\Title\TitleFactory;
* @ingroup Cache
* @since 1.35
*/
-class HtmlCacheUpdater {
+class HTMLCacheUpdater {
/** @var int Seconds between initial and rebound purges; 0 if disabled */
private $reboundDelay;
/** @var bool Whether filesystem-based HTML output caching is enabled */
@@ -126,8 +129,8 @@ class HtmlCacheUpdater {
*
* @param string[]|string $urls URL or list of URLs
* @param int $flags Bit field of class PURGE_* constants
- * [Default: HtmlCacheUpdater::PURGE_PRESEND]
- * @param mixed[] $unless Optional map of (HtmlCacheUpdater::UNLESS_* constant => value)
+ * [Default: HTMLCacheUpdater::PURGE_PRESEND]
+ * @param mixed[] $unless Optional map of (HTMLCacheUpdater::UNLESS_* constant => value)
*/
public function purgeUrls( $urls, $flags = self::PURGE_PRESEND, array $unless = [] ) {
$minFreshCacheMtime = $unless[self::UNLESS_CACHE_MTIME_AFTER] ?? null;
@@ -158,8 +161,8 @@ class HtmlCacheUpdater {
* @param Traversable|PageReference[]|PageReference $pages PageReference or iterator yielding
* PageReference instances
* @param int $flags Bit field of class PURGE_* constants
- * [Default: HtmlCacheUpdater::PURGE_PRESEND]
- * @param mixed[] $unless Optional map of (HtmlCacheUpdater::UNLESS_* constant => value)
+ * [Default: HTMLCacheUpdater::PURGE_PRESEND]
+ * @param mixed[] $unless Optional map of (HTMLCacheUpdater::UNLESS_* constant => value)
*/
public function purgeTitleUrls( $pages, $flags = self::PURGE_PRESEND, array $unless = [] ) {
$pages = is_iterable( $pages ) ? $pages : [ $pages ];
@@ -253,3 +256,6 @@ class HtmlCacheUpdater {
return $urls;
}
}
+
+/** @deprecated since 1.42 */
+class_alias( HTMLCacheUpdater::class, 'HtmlCacheUpdater' );
diff --git a/includes/cache/Hook/HtmlCacheUpdaterAppendUrlsHook.php b/includes/cache/Hook/HtmlCacheUpdaterAppendUrlsHook.php
index 9d0fd20c9c68..4a9bbb6c5eda 100644
--- a/includes/cache/Hook/HtmlCacheUpdaterAppendUrlsHook.php
+++ b/includes/cache/Hook/HtmlCacheUpdaterAppendUrlsHook.php
@@ -19,7 +19,7 @@ interface HtmlCacheUpdaterAppendUrlsHook {
* re-render of the same content. For example, after a direct revision to the content the
* history page will need to be purged. However when re-rendering after a cascading change
* from a template, only URLs that render content need purging. The $mode will be either
- * HtmlCacheUpdater::PURGE_URLS_LINKSUPDATE_ONLY or 0.
+ * HTMLCacheUpdater::PURGE_URLS_LINKSUPDATE_ONLY or 0.
*
* @since 1.35
*
diff --git a/includes/cache/LinkBatch.php b/includes/cache/LinkBatch.php
index 42fadf06fc63..0e29c15cab00 100644
--- a/includes/cache/LinkBatch.php
+++ b/includes/cache/LinkBatch.php
@@ -21,7 +21,10 @@
* @ingroup Cache
*/
-use MediaWiki\Cache\CacheKeyHelper;
+namespace MediaWiki\Cache;
+
+use InvalidArgumentException;
+use Language;
use MediaWiki\Linker\LinksMigration;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\Page\PageIdentityValue;
@@ -30,6 +33,7 @@ use MediaWiki\Page\ProperPageIdentity;
use MediaWiki\Title\TitleFormatter;
use MediaWiki\Title\TitleValue;
use Psr\Log\LoggerInterface;
+use RuntimeException;
use Wikimedia\Assert\Assert;
use Wikimedia\Rdbms\IConnectionProvider;
use Wikimedia\Rdbms\IResultWrapper;
@@ -375,3 +379,6 @@ class LinkBatch {
return $db->makeWhereFrom2d( $this->data, $blNamespace, $blTitle );
}
}
+
+/** @deprecated since 1.42 */
+class_alias( LinkBatch::class, 'LinkBatch' );
diff --git a/includes/cache/LinkCache.php b/includes/cache/LinkCache.php
index 8b353febe721..339bb411350b 100644
--- a/includes/cache/LinkCache.php
+++ b/includes/cache/LinkCache.php
@@ -21,6 +21,12 @@
* @ingroup Cache
*/
+namespace MediaWiki\Cache;
+
+use DBAccessObjectUtils;
+use IDBAccessObject;
+use InvalidArgumentException;
+use MapCacheLRU;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MainConfigNames;
use MediaWiki\MediaWikiServices;
@@ -33,6 +39,8 @@ use MediaWiki\Title\TitleValue;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
+use stdClass;
+use WANObjectCache;
use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\ILoadBalancer;
@@ -573,3 +581,6 @@ class LinkCache implements LoggerAwareInterface {
$this->entries->clear();
}
}
+
+/** @deprecated since 1.42 */
+class_alias( LinkCache::class, 'LinkCache' );
diff --git a/includes/cache/UserCache.php b/includes/cache/UserCache.php
index afbf75c93220..9a75cc6799dd 100644
--- a/includes/cache/UserCache.php
+++ b/includes/cache/UserCache.php
@@ -21,7 +21,8 @@
* @ingroup Cache
*/
-use MediaWiki\Cache\LinkBatchFactory;
+namespace MediaWiki\Cache;
+
use MediaWiki\MediaWikiServices;
use Psr\Log\LoggerInterface;
use Wikimedia\Rdbms\IConnectionProvider;
@@ -175,3 +176,6 @@ class UserCache {
return ( in_array( $type, $options ) && !isset( $this->typesCached[$uid][$type] ) );
}
}
+
+/** @deprecated since 1.42 */
+class_alias( UserCache::class, 'UserCache' );
diff --git a/includes/changes/CategoryMembershipChange.php b/includes/changes/CategoryMembershipChange.php
index 03f5266f4083..042481a21fce 100644
--- a/includes/changes/CategoryMembershipChange.php
+++ b/includes/changes/CategoryMembershipChange.php
@@ -1,5 +1,6 @@
<?php
+use MediaWiki\Cache\BacklinkCache;
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Title\Title;
diff --git a/includes/changes/RecentChange.php b/includes/changes/RecentChange.php
index 4d61fb250756..beae24cff368 100644
--- a/includes/changes/RecentChange.php
+++ b/includes/changes/RecentChange.php
@@ -633,8 +633,16 @@ class RecentChange implements Taggable {
if ( $this->getAttribute( 'rc_patrolled' ) ) {
return [];
}
- // Actually set the 'patrolled' flag in RC
- $this->reallyMarkPatrolled();
+ // Attempt to set the 'patrolled' flag in RC database
+ $affectedRowCount = $this->reallyMarkPatrolled();
+
+ if ( $affectedRowCount === 0 ) {
+ // Query succeeded but no rows change, e.g. another request
+ // patrolled the same change just before us.
+ // Avoid duplicate log entry (T196182).
+ return [];
+ }
+
// Log this patrol event
PatrolLog::record( $this, false, $performer->getUser(), $tags );
@@ -646,15 +654,25 @@ class RecentChange implements Taggable {
/**
* Mark this RecentChange patrolled, without error checking
- * @return int Number of affected rows
+ *
+ * @return int Number of database rows changed, usually 1, but 0 if
+ * another request already patrolled it in the mean time.
*/
public function reallyMarkPatrolled() {
$dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase();
$dbw->newUpdateQueryBuilder()
->update( 'recentchanges' )
->set( [ 'rc_patrolled' => self::PRC_PATROLLED ] )
- ->where( [ 'rc_id' => $this->getAttribute( 'rc_id' ) ] )
+ ->where( [
+ 'rc_id' => $this->getAttribute( 'rc_id' ),
+ 'rc_patrolled' => self::PRC_UNPATROLLED,
+ ] )
->caller( __METHOD__ )->execute();
+ $affectedRowCount = $dbw->affectedRows();
+ // The change was patrolled already, do nothing
+ if ( $affectedRowCount === 0 ) {
+ return 0;
+ }
// Invalidate the page cache after the page has been patrolled
// to make sure that the Patrol link isn't visible any longer!
$this->getTitle()->invalidateCache();
@@ -667,7 +685,7 @@ class RecentChange implements Taggable {
$revertedTagUpdateManager->approveRevertedTagForRevision( $revisionId );
}
- return $dbw->affectedRows();
+ return $affectedRowCount;
}
/**
diff --git a/includes/content/ContentHandler.php b/includes/content/ContentHandler.php
index f9796c2c85f6..36e09f0cbc8a 100644
--- a/includes/content/ContentHandler.php
+++ b/includes/content/ContentHandler.php
@@ -251,7 +251,8 @@ abstract class ContentHandler {
public static function getLocalizedName( $name, Language $lang = null ) {
// Messages: content-model-wikitext, content-model-text,
// content-model-javascript, content-model-css
- $key = "content-model-$name";
+ // Lowercase the name as message keys need to be in lowercase, T358341
+ $key = "content-model-" . strtolower( $name ?? '' );
$msg = wfMessage( $key );
if ( $lang ) {
diff --git a/includes/content/Renderer/ContentRenderer.php b/includes/content/Renderer/ContentRenderer.php
index 6793bbab0414..68add0727520 100644
--- a/includes/content/Renderer/ContentRenderer.php
+++ b/includes/content/Renderer/ContentRenderer.php
@@ -37,7 +37,7 @@ class ContentRenderer {
*
* @param Content $content
* @param PageReference $page
- * @param RevisionRecord|int|null $revision
+ * @param RevisionRecord|null $revision
* @param ParserOptions|null $parserOptions
* @param bool $generateHtml
*
diff --git a/includes/content/WikitextContentHandler.php b/includes/content/WikitextContentHandler.php
index bfe76fbfd86c..42c0ea25646f 100644
--- a/includes/content/WikitextContentHandler.php
+++ b/includes/content/WikitextContentHandler.php
@@ -28,6 +28,7 @@ use MediaWiki\Content\Transform\PreloadTransformParams;
use MediaWiki\Content\Transform\PreSaveTransformParams;
use MediaWiki\Languages\LanguageNameUtils;
use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\Logger\LoggerFactory;
use MediaWiki\Parser\MagicWordFactory;
use MediaWiki\Parser\ParserOutput;
use MediaWiki\Parser\ParserOutputFlags;
@@ -369,8 +370,23 @@ class WikitextContentHandler extends TextContentHandler {
$text = $contentWithoutRedirect->getText();
}
+ $time = -microtime( true );
+
$parserOutput = $parser
->parse( $text, $title, $parserOptions, true, true, $revId );
+ $time += microtime( true );
+
+ // Timing hack
+ if ( $time > 3 ) {
+ // TODO: Use Parser's logger (once it has one)
+ $channel = $parserOptions->getUseParsoid() ? 'slow-parsoid' : 'slow-parse';
+ $logger = LoggerFactory::getInstance( $channel );
+ $logger->info( 'Parsing {title} was slow, took {time} seconds', [
+ 'time' => number_format( $time, 2 ),
+ 'title' => (string)$title,
+ 'trigger' => $parserOptions->getRenderReason(),
+ ] );
+ }
// T330667: Record the fact that we used the value of
// 'useParsoid' to influence this parse. Note that
diff --git a/includes/deferred/CdnCacheUpdate.php b/includes/deferred/CdnCacheUpdate.php
index 7ce750f9aaf0..d02417d6e673 100644
--- a/includes/deferred/CdnCacheUpdate.php
+++ b/includes/deferred/CdnCacheUpdate.php
@@ -159,7 +159,7 @@ class CdnCacheUpdate implements DeferrableUpdate, MergeableUpdate {
$services = MediaWikiServices::getInstance();
/** @var PageReference $page */
- // Avoid multiple queries for HtmlCacheUpdater::getUrls() call
+ // Avoid multiple queries for HTMLCacheUpdater::getUrls() call
$lb = $services->getLinkBatchFactory()->newLinkBatch();
foreach ( $this->pageTuples as [ $page, ] ) {
$lb->addObj( $page );
diff --git a/includes/deferred/LinksUpdate/LinksUpdate.php b/includes/deferred/LinksUpdate/LinksUpdate.php
index 0970966d175f..66cfa1c1fe51 100644
--- a/includes/deferred/LinksUpdate/LinksUpdate.php
+++ b/includes/deferred/LinksUpdate/LinksUpdate.php
@@ -22,9 +22,9 @@
namespace MediaWiki\Deferred\LinksUpdate;
-use BacklinkCache;
use IDBAccessObject;
use Job;
+use MediaWiki\Cache\BacklinkCache;
use MediaWiki\Deferred\AutoCommitUpdate;
use MediaWiki\Deferred\DataUpdate;
use MediaWiki\Deferred\DeferredUpdates;
diff --git a/includes/editpage/EditPage.php b/includes/editpage/EditPage.php
index 0944fbb737fa..382c5e70fc4a 100644
--- a/includes/editpage/EditPage.php
+++ b/includes/editpage/EditPage.php
@@ -4116,6 +4116,8 @@ class EditPage implements IEditObject {
MediaWikiServices::getInstance()->getStatsFactory()
->getCounter( 'edit_failure_total' )
->setLabel( 'cause', $failureType )
+ ->setLabel( 'namespace', 'n/a' )
+ ->setLabel( 'user_bucket', 'n/a' )
->copyToStatsdAt( 'edit.failures.' . $failureType )
->increment();
}
@@ -4600,7 +4602,7 @@ class EditPage implements IEditObject {
$this->editConflictHelper = new TextConflictHelper(
$this->getTitle(),
$this->getContext()->getOutput(),
- MediaWikiServices::getInstance()->getStatsdDataFactory(),
+ MediaWikiServices::getInstance()->getStatsFactory(),
$label,
MediaWikiServices::getInstance()->getContentHandlerFactory()
);
diff --git a/includes/editpage/TextConflictHelper.php b/includes/editpage/TextConflictHelper.php
index 3862b3055026..90ffdd127ae5 100644
--- a/includes/editpage/TextConflictHelper.php
+++ b/includes/editpage/TextConflictHelper.php
@@ -29,6 +29,7 @@ use MediaWiki\Output\OutputPage;
use MediaWiki\Title\Title;
use MediaWiki\User\User;
use MWUnknownContentModelException;
+use Wikimedia\Stats\StatsFactory;
/**
* Helper for displaying edit conflicts in text content models to users
@@ -59,7 +60,7 @@ class TextConflictHelper {
protected $out;
/**
- * @var IBufferingStatsdDataFactory
+ * @var IBufferingStatsdDataFactory|StatsFactory
*/
protected $stats;
@@ -86,14 +87,14 @@ class TextConflictHelper {
/**
* @param Title $title
* @param OutputPage $out
- * @param IBufferingStatsdDataFactory $stats
+ * @param IBufferingStatsdDataFactory|StatsFactory $stats
* @param string $submitLabel
* @param IContentHandlerFactory $contentHandlerFactory Required param with legacy support
*
* @throws MWUnknownContentModelException
*/
public function __construct(
- Title $title, OutputPage $out, IBufferingStatsdDataFactory $stats, $submitLabel,
+ Title $title, OutputPage $out, $stats, $submitLabel,
IContentHandlerFactory $contentHandlerFactory
) {
$this->title = $title;
@@ -136,18 +137,36 @@ class TextConflictHelper {
* @param User|null $user
*/
public function incrementConflictStats( User $user = null ) {
- $this->stats->increment( 'edit.failures.conflict' );
+ $namespace = 'n/a';
+ $userBucket = 'n/a';
+ $statsdMetrics = [ 'edit.failures.conflict' ];
+
// Only include 'standard' namespaces to avoid creating unknown numbers of statsd metrics
if (
$this->title->getNamespace() >= NS_MAIN &&
$this->title->getNamespace() <= NS_CATEGORY_TALK
) {
- $this->stats->increment(
- 'edit.failures.conflict.byNamespaceId.' . $this->title->getNamespace()
- );
+ // getNsText() returns empty string if getNamespace() === NS_MAIN
+ $namespace = $this->title->getNsText() ?: 'Main';
+ $statsdMetrics[] = 'edit.failures.conflict.byNamespaceId.' . $this->title->getNamespace();
}
if ( $user ) {
- $this->incrementStatsByUserEdits( $user->getEditCount(), 'edit.failures.conflict' );
+ $userBucket = $this->getUserBucket( $user->getEditCount() );
+ $statsdMetrics[] = 'edit.failures.conflict.byUserEdits.' . $userBucket;
+ }
+ if ( $this->stats instanceof StatsFactory ) {
+ $this->stats->getCounter( 'edit_failure_total' )
+ ->setLabel( 'cause', 'conflict' )
+ ->setLabel( 'namespace', $namespace )
+ ->setLabel( 'user_bucket', $userBucket )
+ ->copyToStatsdAt( $statsdMetrics )
+ ->increment();
+ }
+
+ if ( $this->stats instanceof IBufferingStatsdDataFactory ) {
+ foreach ( $statsdMetrics as $metric ) {
+ $this->stats->increment( $metric );
+ }
}
}
@@ -156,41 +175,72 @@ class TextConflictHelper {
* @param User|null $user
*/
public function incrementResolvedStats( User $user = null ) {
- $this->stats->increment( 'edit.failures.conflict.resolved' );
+ $namespace = 'n/a';
+ $userBucket = 'n/a';
+ $statsdMetrics = [ 'edit.failures.conflict.resolved' ];
+
// Only include 'standard' namespaces to avoid creating unknown numbers of statsd metrics
if (
$this->title->getNamespace() >= NS_MAIN &&
$this->title->getNamespace() <= NS_CATEGORY_TALK
) {
- $this->stats->increment(
- 'edit.failures.conflict.resolved.byNamespaceId.' . $this->title->getNamespace()
- );
+ // getNsText() returns empty string if getNamespace() === NS_MAIN
+ $namespace = $this->title->getNsText() ?: 'Main';
+ $statsdMetrics[] = 'edit.failures.conflict.resolved.byNamespaceId.' . $this->title->getNamespace();
}
+
if ( $user ) {
- $this->incrementStatsByUserEdits(
- $user->getEditCount(),
- 'edit.failures.conflict.resolved'
- );
+ $userBucket = $this->getUserBucket( $user->getEditCount() );
+ $statsdMetrics[] = 'edit.failures.conflict.resolved.byUserEdits.' . $userBucket;
+ }
+
+ if ( $this->stats instanceof StatsFactory ) {
+ $this->stats->getCounter( 'edit_failure_resolved_total' )
+ ->setLabel( 'cause', 'conflict' )
+ ->setLabel( 'namespace', $namespace )
+ ->setLabel( 'user_bucket', $userBucket )
+ ->copyToStatsdAt( $statsdMetrics )
+ ->increment();
+ }
+
+ if ( $this->stats instanceof IBufferingStatsdDataFactory ) {
+ foreach ( $statsdMetrics as $metric ) {
+ $this->stats->increment( $metric );
+ }
}
}
/**
+ * Retained temporarily for backwards-compatibility.
+ *
+ * This action should be moved into incrementConflictStats, incrementResolvedStats.
+ *
+ * @deprecated since 1.42, do not use
* @param int|null $userEdits
* @param string $keyPrefixBase
*/
protected function incrementStatsByUserEdits( $userEdits, $keyPrefixBase ) {
+ if ( $this->stats instanceof IBufferingStatsdDataFactory ) {
+ $this->stats->increment( $keyPrefixBase . '.byUserEdits.' . $this->getUserBucket( $userEdits ) );
+ }
+ }
+
+ /**
+ * @param int|null $userEdits
+ * @return string
+ */
+ protected function getUserBucket( ?int $userEdits ): string {
if ( $userEdits === null ) {
- $userBucket = 'anon';
+ return 'anon';
} elseif ( $userEdits > 200 ) {
- $userBucket = 'over200';
+ return 'over200';
} elseif ( $userEdits > 100 ) {
- $userBucket = 'over100';
+ return 'over100';
} elseif ( $userEdits > 10 ) {
- $userBucket = 'over10';
+ return 'over10';
} else {
- $userBucket = 'under11';
+ return 'under11';
}
- $this->stats->increment( $keyPrefixBase . '.byUserEdits.' . $userBucket );
}
/**
diff --git a/includes/filerepo/FileRepo.php b/includes/filerepo/FileRepo.php
index 3bf766c9c70e..e3c302f84edf 100644
--- a/includes/filerepo/FileRepo.php
+++ b/includes/filerepo/FileRepo.php
@@ -887,7 +887,7 @@ class FileRepo {
public function getDescriptionStylesheetUrl() {
if ( isset( $this->scriptDirUrl ) ) {
// Must match canonical query parameter order for optimum caching
- // See HtmlCacheUpdater::getUrls
+ // See HTMLCacheUpdater::getUrls
return $this->makeUrl( 'title=MediaWiki:Filepage.css&action=raw&ctype=text/css' );
}
diff --git a/includes/installer/i18n/qqq.json b/includes/installer/i18n/qqq.json
index d724399d44d9..db16abe2ecc0 100644
--- a/includes/installer/i18n/qqq.json
+++ b/includes/installer/i18n/qqq.json
@@ -72,7 +72,7 @@
"config-env-bad": "See also:\n* {{msg-mw|Config-env-good}}",
"config-env-php": "Parameters:\n* $1 - the version of PHP that has been installed\nSee also:\n* {{msg-mw|config-env-php-toolow}}",
"config-env-icu": "ICU refers to [[:wikipedia:International Components for Unicode|International Components for Unicode]], a set of libraries for support of Unicode and other internationalization aspects. Parameters:\n* $1 - the version of ICU that has been installed\n* $2 - the version of Unicode supported by the installed ICU version",
- "config-no-db": "{{doc-important|Do not translate \"<code>./configure --with-mysqli</code>\" and \"<code>php-mysql</code>\".}}\nParameters:\n* $1 is comma separated list of database types supported by MediaWiki.\n* $2 is the count of items in $1 - for use in plural.",
+ "config-no-db": "{{doc-important|Do not translate \"<code>./configure --with-mysqli</code>\" and \"<code>php-mysql</code>\".}}\nParameters:\n* $1 is comma-separated list of database types supported by MediaWiki.\n* $2 is the count of items in $1 - for use in plural.",
"config-outdated-sqlite": "Used as warning. Parameters:\n* $2 - the version of SQLite that has been installed\n* $1 - minimum version",
"config-no-fts3": "A \"[[:wikipedia:Front and back ends|backend]]\" is a system or component that ordinary users don't interact with directly and don't need to know about, and that is responsible for a distinct task or service - for example, a storage back-end is a generic system for storing data which other applications can use. Possible alternatives for back-end are \"system\" or \"service\", or (depending on context and language) even leave it untranslated.",
"config-pcre-invalid-newline": "PCRE2 is the name of a programmers' library for regular expressions; it can probably be used without translation. <code>--enable-newline-is-any</code> is a configuration option that can be specified for the mentioned library, and should be left untranslated.\n{{Related|Config-fatal}}",
diff --git a/includes/jobqueue/JobRunner.php b/includes/jobqueue/JobRunner.php
index b6e35d78ddfe..337386fa1696 100644
--- a/includes/jobqueue/JobRunner.php
+++ b/includes/jobqueue/JobRunner.php
@@ -22,6 +22,7 @@
*/
use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Deferred\DeferredUpdates;
use MediaWiki\Http\Telemetry;
diff --git a/includes/jobqueue/jobs/PublishStashedFileJob.php b/includes/jobqueue/jobs/PublishStashedFileJob.php
index 1207fb11e79f..982f68f5cab3 100644
--- a/includes/jobqueue/jobs/PublishStashedFileJob.php
+++ b/includes/jobqueue/jobs/PublishStashedFileJob.php
@@ -16,13 +16,9 @@
* http://www.gnu.org/copyleft/gpl.html
*
* @file
+ * @defgroup JobQueue JobQueue
*/
-use MediaWiki\Context\RequestContext;
-use MediaWiki\Logger\LoggerFactory;
-use MediaWiki\Status\Status;
-use Wikimedia\ScopedCallback;
-
/**
* Upload a file from the upload stash into the local file repo.
*
@@ -30,134 +26,12 @@ use Wikimedia\ScopedCallback;
* @ingroup JobQueue
*/
class PublishStashedFileJob extends Job implements GenericParameterJob {
+ use UploadJobTrait;
public function __construct( array $params ) {
parent::__construct( 'PublishStashedFile', $params );
$this->removeDuplicates = true;
- }
-
- public function run() {
- $scope = RequestContext::importScopedSession( $this->params['session'] );
- $this->addTeardownCallback( static function () use ( &$scope ) {
- ScopedCallback::consume( $scope ); // T126450
- } );
-
- $context = RequestContext::getMain();
- $user = $context->getUser();
- try {
- if ( !$user->isRegistered() ) {
- $this->setLastError( "Could not load the author user from session." );
-
- return false;
- }
-
- $startingStatus = UploadBase::getSessionStatus( $user, $this->params['filekey'] );
- // Warn if in wrong stage, but still continue. User may be able to trigger
- // this by retrying after failure.
- if (
- !$startingStatus ||
- ( $startingStatus['result'] ?? '' ) !== 'Poll' ||
- ( $startingStatus['stage'] ?? '' ) !== 'queued'
- ) {
- $logger = LoggerFactory::getInstance( 'upload' );
- $logger->warning( "Tried to publish upload that is in stage {stage}/{result}",
- [
- 'stage' => $startingStatus['stage'] ?? '-',
- 'result' => $startingStatus['result'] ?? '-',
- 'status' => (string)( $startingStatus['status'] ?? '-' ),
- 'filekey' => $this->params['filekey'],
- 'filename' => $this->params['filename'],
- 'user' => $user->getName(),
- ]
- );
- }
-
- UploadBase::setSessionStatus(
- $user,
- $this->params['filekey'],
- [ 'result' => 'Poll', 'stage' => 'publish', 'status' => Status::newGood() ]
- );
-
- $upload = new UploadFromStash( $user );
- // @todo initialize() causes a GET, ideally we could frontload the antivirus
- // checks and anything else to the stash stage (which includes concatenation and
- // the local file is thus already there). That way, instead of GET+PUT, there could
- // just be a COPY operation from the stash to the public zone.
- $upload->initialize( $this->params['filekey'], $this->params['filename'] );
-
- // Check if the local file checks out (this is generally a no-op)
- $verification = $upload->verifyUpload();
- if ( $verification['status'] !== UploadBase::OK ) {
- $status = Status::newFatal( 'verification-error' );
- $status->value = [ 'verification' => $verification ];
- UploadBase::setSessionStatus(
- $user,
- $this->params['filekey'],
- [ 'result' => 'Failure', 'stage' => 'publish', 'status' => $status ]
- );
- $this->setLastError( "Could not verify upload." );
-
- return false;
- }
-
- // Upload the stashed file to a permanent location
- $status = $upload->performUpload(
- $this->params['comment'],
- $this->params['text'],
- $this->params['watch'],
- $user,
- $this->params['tags'] ?? [],
- $this->params['watchlistexpiry'] ?? null
- );
- if ( !$status->isGood() ) {
- UploadBase::setSessionStatus(
- $user,
- $this->params['filekey'],
- [ 'result' => 'Failure', 'stage' => 'publish', 'status' => $status ]
- );
- $this->setLastError( $status->getWikiText( false, false, 'en' ) );
-
- return false;
- }
-
- // Build the image info array while we have the local reference handy
- $apiUpload = ApiUpload::getDummyInstance();
- $imageInfo = $apiUpload->getUploadImageInfo( $upload );
-
- // Cleanup any temporary local file
- $upload->cleanupTempFile();
-
- // Cache the info so the user doesn't have to wait forever to get the final info
- UploadBase::setSessionStatus(
- $user,
- $this->params['filekey'],
- [
- 'result' => 'Success',
- 'stage' => 'publish',
- 'filename' => $upload->getLocalFile()->getName(),
- 'imageinfo' => $imageInfo,
- 'status' => Status::newGood()
- ]
- );
- } catch ( Exception $e ) {
- UploadBase::setSessionStatus(
- $user,
- $this->params['filekey'],
- [
- 'result' => 'Failure',
- 'stage' => 'publish',
- 'status' => Status::newFatal( 'api-error-publishfailed' )
- ]
- );
- $this->setLastError( get_class( $e ) . ": " . $e->getMessage() );
- // To prevent potential database referential integrity issues.
- // See T34551.
- MWExceptionHandler::rollbackPrimaryChangesAndLog( $e );
-
- return false;
- }
-
- return true;
+ $this->initialiseUploadJob( $this->params['filekey'] );
}
public function getDeduplicationInfo() {
@@ -169,7 +43,37 @@ class PublishStashedFileJob extends Job implements GenericParameterJob {
return $info;
}
- public function allowRetries() {
- return false;
+ /**
+ * Get the parameters for job logging
+ *
+ * @param Status[] $status
+ * @return array
+ */
+ public function logJobParams( $status ) {
+ return [
+ 'stage' => $status['stage'] ?? '-',
+ 'result' => $status['result'] ?? '-',
+ 'status' => (string)( $status['status'] ?? '-' ),
+ 'filekey' => $this->params['filekey'],
+ 'filename' => $this->params['filename'],
+ 'user' => $this->user->getName(),
+ ];
+ }
+
+ /**
+ * getter for the upload
+ *
+ * @return UploadFromStash
+ */
+ protected function getUpload() {
+ if ( $this->upload === null ) {
+ $this->upload = new UploadFromStash( $this->user );
+ // @todo initialize() causes a GET, ideally we could frontload the antivirus
+ // checks and anything else to the stash stage (which includes concatenation and
+ // the local file is thus already there). That way, instead of GET+PUT, there could
+ // just be a COPY operation from the stash to the public zone.
+ $this->upload->initialize( $this->params['filekey'], $this->params['filename'] );
+ }
+ return $this->upload;
}
}
diff --git a/includes/jobqueue/jobs/UploadJobTrait.php b/includes/jobqueue/jobs/UploadJobTrait.php
new file mode 100644
index 000000000000..979f066a5072
--- /dev/null
+++ b/includes/jobqueue/jobs/UploadJobTrait.php
@@ -0,0 +1,280 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @defgroup JobQueue JobQueue
+ */
+
+use MediaWiki\Context\RequestContext;
+use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\Status\Status;
+use Mediawiki\User\User;
+use Wikimedia\ScopedCallback;
+
+/**
+ * Common functionality for async uploads
+ *
+ * @ingroup Upload
+ * @ingroup JobQueue
+ */
+trait UploadJobTrait {
+ /** @var User|null */
+ private $user;
+
+ /** @var string */
+ private $cacheKey;
+
+ /** @var UploadBase */
+ private $upload;
+
+ /** @var array The job parameters */
+ public $params;
+
+ /**
+ * Set up the job
+ *
+ * @param string $cacheKey
+ * @return void
+ */
+ protected function initialiseUploadJob( $cacheKey ): void {
+ $this->cacheKey = $cacheKey;
+ $this->user = null;
+ }
+
+ /**
+ * Do not allow retries on jobs by default.
+ *
+ * @return bool
+ */
+ public function allowRetries(): bool {
+ return false;
+ }
+
+ /**
+ * Run the job
+ *
+ * @return bool
+ */
+ public function run(): bool {
+ $this->user = $this->getUserFromSession();
+ if ( $this->user === null ) {
+ return false;
+ }
+
+ try {
+ // Check the initial status of the upload
+ $startingStatus = UploadBase::getSessionStatus( $this->user, $this->cacheKey );
+ // Warn if in wrong stage, but still continue. User may be able to trigger
+ // this by retrying after failure.
+ if (
+ !$startingStatus ||
+ ( $startingStatus['result'] ?? '' ) !== 'Poll' ||
+ ( $startingStatus['stage'] ?? '' ) !== 'queued'
+ ) {
+ $logger = LoggerFactory::getInstance( 'upload' );
+ $logger->warning( "Tried to publish upload that is in stage {stage}/{result}",
+ $this->logJobParams( $startingStatus )
+ );
+ }
+
+ // Fetch the file if needed
+ if ( !$this->fetchFile() ) {
+ return false;
+ }
+
+ // Verify the upload is valid
+ if ( !$this->verifyUpload() ) {
+ return false;
+ }
+
+ // Actually upload the file
+ if ( !$this->performUpload() ) {
+ return false;
+ }
+
+ // All done
+ $this->setStatusDone();
+
+ // Cleanup any temporary local file
+ $this->getUpload()->cleanupTempFile();
+
+ } catch ( Exception $e ) {
+ $this->setStatus( 'publish', 'Failure', Status::newFatal( 'api-error-publishfailed' ) );
+ $this->setLastError( get_class( $e ) . ": " . $e->getMessage() );
+ // To prevent potential database referential integrity issues.
+ // See T34551.
+ MWExceptionHandler::rollbackPrimaryChangesAndLog( $e );
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get user data from the session key
+ *
+ * @return User|null
+ */
+ private function getUserFromSession() {
+ $scope = RequestContext::importScopedSession( $this->params['session'] );
+ $this->addTeardownCallback( static function () use ( &$scope ) {
+ ScopedCallback::consume( $scope ); // T126450
+ } );
+
+ $context = RequestContext::getMain();
+ $user = $context->getUser();
+ if ( !$user->isRegistered() ) {
+ $this->setLastError( "Could not load the author user from session." );
+
+ return null;
+ }
+ return $user;
+ }
+
+ /**
+ * Set the upload status
+ *
+ * @param string $stage
+ * @param string $result
+ * @param Status|null $status
+ * @param array $additionalInfo
+ *
+ */
+ private function setStatus( $stage, $result = 'Poll', $status = null, $additionalInfo = [] ) {
+ // We're most probably not running in a job.
+ // @todo maybe throw an exception?
+ if ( $this->user === null ) {
+ return;
+ }
+ if ( $status === null ) {
+ $status = Status::newGood();
+ }
+ $info = [ 'result' => $result, 'stage' => $stage, 'status' => $status ];
+ $info += $additionalInfo;
+ UploadBase::setSessionStatus(
+ $this->user,
+ $this->cacheKey,
+ $info
+ );
+ }
+
+ /**
+ * Ensure we have the file available. A noop here.
+ *
+ * @return bool
+ */
+ protected function fetchFile(): bool {
+ // make sure the upload file is here. This is a noop in most cases.
+ $this->getUpload()->fetchFile();
+ $this->setStatus( 'publish' );
+ // We really don't care as this is, as mentioned, generally a noop.
+ // When that's not the case, classes will need to override this method anyways.
+ return true;
+ }
+
+ /**
+ * Verify the upload is ok
+ *
+ * @return bool
+ */
+ private function verifyUpload(): bool {
+ // Check if the local file checks out (this is generally a no-op)
+ $verification = $this->getUpload()->verifyUpload();
+ if ( $verification['status'] !== UploadBase::OK ) {
+ $status = Status::newFatal( 'verification-error' );
+ $status->value = [ 'verification' => $verification ];
+ $this->setStatus( 'publish', 'Failure', $status );
+ $this->setLastError( "Could not verify upload." );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Upload the stashed file to a permanent location
+ *
+ * @return bool
+ */
+ private function performUpload(): bool {
+ if ( $this->user === null ) {
+ return false;
+ }
+ $status = $this->getUpload()->performUpload(
+ $this->params['comment'],
+ $this->params['text'],
+ $this->params['watch'],
+ $this->user,
+ $this->params['tags'] ?? [],
+ $this->params['watchlistexpiry'] ?? null
+ );
+ if ( !$status->isGood() ) {
+ $this->setStatus( 'publish', 'Failure', $status );
+ $this->setLastError( $status->getWikiText( false, false, 'en' ) );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Set the status at the end or processing
+ *
+ */
+ private function setStatusDone() {
+ // Build the image info array while we have the local reference handy
+ $imageInfo = ApiUpload::getDummyInstance()->getUploadImageInfo( $this->getUpload() );
+
+ // Cache the info so the user doesn't have to wait forever to get the final info
+ $this->setStatus(
+ 'publish',
+ 'Success',
+ Status::newGood(),
+ [ 'filename' => $this->getUpload()->getLocalFile()->getName(), 'imageinfo' => $imageInfo ]
+ );
+ }
+
+ /**
+ * Getter for the upload. Needs to be implemented by the job class
+ *
+ * @return UploadBase
+ */
+ abstract protected function getUpload(): UploadBase;
+
+ /**
+ * Get the job parameters for logging. Needs to be implemented by the job class.
+ *
+ * @param Status[] $status
+ * @return array
+ */
+ abstract protected function logJobParams( $status ): array;
+
+ /**
+ * This is actually implemented in the Job class
+ *
+ * @param mixed $error
+ * @return void
+ */
+ abstract protected function setLastError( $error ): void;
+
+ /**
+ * This is actually implemented in the Job class
+ *
+ * @param callable $callback
+ * @return void
+ */
+ abstract protected function addTeardownCallback( $callback ): void;
+
+}
diff --git a/includes/libs/ParamValidator/i18n/hu.json b/includes/libs/ParamValidator/i18n/hu.json
index 6e2f130dcece..60320105b70b 100644
--- a/includes/libs/ParamValidator/i18n/hu.json
+++ b/includes/libs/ParamValidator/i18n/hu.json
@@ -1,24 +1,63 @@
{
"@metadata": {
"authors": [
+ "Dj",
"Tacsipacsi"
]
},
+ "paramvalidator-badbool": "A „$1” logikai paraméterhez megadott „$2” érték nem megfelelő. Adjon át $3 értéket igaznak, $5 értéket hamisnak.",
+ "paramvalidator-badexpiry": "A(z) „$1” lejárati paraméternél megadott „$2” érték érvénytelen.",
+ "paramvalidator-badexpiry-duration": "A(z) <var>$1</var> paraméter adott „$2” értéke meghaladja a „$3” maximális értéket.",
+ "paramvalidator-badexpiry-duration-max": "A(z) <var>$1</var> paraméter „$2” értéke meghaladja a „$3” maximális értéket. A maximális érték kerül használatra.",
+ "paramvalidator-badexpiry-past": "A „$2” lejárati paraméter „$1” értéke a múltban van.",
+ "paramvalidator-badfloat": "A(z) „$1” lebegőpontos szám paraméternél megadott „$2” érték érvénytelen.",
+ "paramvalidator-badfloat-notfinite": "A(z) „$1” lebegőpontos szám paraméternél megadott „$2” érték túl nagy, vagy nem szám.",
+ "paramvalidator-badinteger": "A(z) „$1” egész szám paraméternél megadott „$2” érték érvénytelen.",
+ "paramvalidator-badtimestamp": "A(z) „$1” időbélyeg paraméternél megadott „$2” érték érvénytelen.",
+ "paramvalidator-badupload-cantwrite": "A(z) „$1” paraméternél megadott fájlt nem lehet feldolgozás céljából eltárolni a szerver hibás konfigurációs miatt (az írás sikertelen).",
+ "paramvalidator-badupload-formsize": "A(z) „$1” paraméternél megadott fájl meghaladja az ügyfél által megadott maximumot.",
+ "paramvalidator-badupload-inisize": "A(z) „$1” paraméternél megadott feltöltött fájl meghaladja a szerver maximumát, ami $3.",
+ "paramvalidator-badupload-nofile": "A(z) „$1” paraméternél nem lett megadva fájl.",
+ "paramvalidator-badupload-notmpdir": "A(z) „$1” paraméternél megadott fájlt nem lehet feldolgozás céljából eltárolni a szerver hibás konfigurációs miatt (nincs ideiglenes könyvtár).",
+ "paramvalidator-badupload-notupload": "A „$1” fájlfeltöltési paraméter nem fájlfeltöltés; ügyeljél arra, hogy multipart\n/form-data-t használj a POST-hoz, és szerepeljen benne egy fájlnév a Content-Disposition fejlécben.",
+ "paramvalidator-badupload-partial": "A(z) „$1” paraméternél megadott fájlt csak részben került feltöltésre.",
+ "paramvalidator-badupload-phpext": "A(z) „$1” paraméternél megadott fájl feltöltét megakadályozta egy PHP-kiterjesztés.",
+ "paramvalidator-badvalue-enummulti": "A(z) „$1” paraméternél megadott „$2” érték érvénytelen.{{PLURAL:$4|Csak „$3” engedélyezett|Az alábbi értékek engedélyezettek: $3}}",
+ "paramvalidator-badvalue-enumnotmulti": "A „$1” paraméterhez megadott érték nem felismerhető: $2.",
+ "paramvalidator-deprecated-value": "A(z) „$1” paraméternél megadott „$2” érték elavult.",
+ "paramvalidator-emptystring": "üres karakterlánc",
+ "paramvalidator-maxbytes": "A(z) „$1” paraméternél megadott érték nem lehet {{PLURAL:$3|$3}} bájtnál hosszabb ($4 volt).",
+ "paramvalidator-maxchars": "A(z) „$1” paraméternél megadott érték nem lehet mint {{PLURAL:$3|$3}} karakternél hosszabb ($4 volt).",
+ "paramvalidator-missingparam": "A(z) „$1” paramétert be kell állítani.",
+ "paramvalidator-notmulti": "A(z) „$1” paraméter csak egyetlen értékes fogad el. Az U+001F többértékű elválasztás csak többértékű paramétereknél használható.",
+ "paramvalidator-outofrange-max": "A(z) „$1” paraméternél megadott „$2” érték nem lehet nagyobb, mint $4.",
+ "paramvalidator-outofrange-minmax": "A(z) „$1” paraméternél megadott „$2” értéknek $3 és $4 közé kell esnie.",
+ "paramvalidator-outofrange-min": "A(z) „$1” paraméternél megadott „$2” érték nem lehet kevesebb, mint $3.",
+ "paramvalidator-param-deprecated": "A(z) „$1” paraméter elavult.",
+ "paramvalidator-toomanyvalues": "Túl sok megadott érték a „$1” paraméternél. A limit $2.",
+ "paramvalidator-unclearnowtimestamp": "A(z) „$1” paraméternél megadott „$2” érték már elavult. Ha valamilyen oknál fogva kifejezetten meg kell adnia az aktuális időt anélkül, hogy azt kliensoldalon számítaná ki, használja a „now” lehetőséget.",
+ "paramvalidator-unrecognizedvalues": "Felismerhetetlen {{PLURAL:$4|érték|értékek}} „$1” paraméternél: $3",
"paramvalidator-help-default": "Alapértelmezett: $1",
"paramvalidator-help-default-empty": "Alapértelmezett: (üres)",
"paramvalidator-help-deprecated": "Ez a paraméter elavult.",
+ "paramvalidator-help-multi-separate": "Az értékeket \"|\"-vel válaszd el, vagy a lista elé tegyél U+001F karaktert, és válaszd el U+001F karakterrel.",
+ "paramvalidator-help-multi-max": "Az értékek maximális száma {{PLURAL:$1|$1}}. ($2 magasabb korláttal rendelkező ügyfelek számára).",
+ "paramvalidator-help-multi-max-simple": "Az értékek maximális száma {{PLURAL:$1|$1}}.",
"paramvalidator-help-multi-all": "Az összes érték megadásához használd a(z) <kbd>$1</kbd> értéket.",
"paramvalidator-help-required": "Ez a paraméter kötelező.",
"paramvalidator-help-type-boolean": "Típus: {{PLURAL:$1|1=logikai|2=logikai értékek listája}}",
"paramvalidator-help-type-enum-can-be-empty": "{{PLURAL:$2|0=Üresnek kell lennie|Lehet üres vagy $1}}",
+ "paramvalidator-help-type-enum": "{{PLURAL:$1|1=A következő értékek egyike|2=Értékek (az U+007C-vel (pipe) elválasztva, vagy a lista előtt U+001F, és elválasztva U+001F karakterrel)}}: $2",
+ "paramvalidator-help-type-expiry": "Típus: {{PLURAL:$1| 1=lejárat|2=lejárati lista}}.\n\nLehet relatív (pl. <kbd>5 hónap</kbd> vagy <kbd>2 hét</kbd> ) vagy abszolút (pl. <kbd>2014-09-18T12:34:56Z</kbd> ). Ha nincs lejárati idő, használd ezeket: $2.",
"paramvalidator-help-type-float": "Típus: {{PLURAL:$1|1=lebegőpontos szám|2=lebegőpontos számok listája}}",
"paramvalidator-help-type-integer": "Típus: {{PLURAL:$1|1=egész szám|2=egész számok listája}}",
"paramvalidator-help-type-limit": "Típus: egész szám vagy „max”",
- "paramvalidator-help-type-number-max": "Az {{PLURAL:$1|1=érték nem lehet nagyobb|2=értékek nem lehetnek nagyobbak}} mint $3.",
+ "paramvalidator-help-type-number-max": "Az {{PLURAL:$1|1=érték nem lehet nagyobb|2=értékek nem lehetnek nagyobbak}}, mint $3.",
"paramvalidator-help-type-number-minmax": "{{PLURAL:$1|1=Az értéknek $2 és $3 között kell lennie.|2=Az értékeknek $2 és $3 között kell lenniük.}}",
"paramvalidator-help-type-number-min": "Az {{PLURAL:$1|1=érték nem lehet kisebb|2=értékek nem lehetnek kisebbek}} mint $2.",
"paramvalidator-help-type-presenceboolean": "Típus: logikai",
"paramvalidator-help-type-string-maxbytes": "Nem lehet hosszabb $1 {{PLURAL:$1|bájtnál}}.",
"paramvalidator-help-type-string-maxchars": "Nem lehet hosszabb $1 {{PLURAL:$1|karakternél}}.",
- "paramvalidator-help-type-timestamp": "Típus: {{PLURAL:$1|1=időbélyeg|2=időbélyegek listája}}"
+ "paramvalidator-help-type-timestamp": "Típus: {{PLURAL:$1|1=időbélyeg|2=időbélyegek listája}}",
+ "paramvalidator-help-type-upload": "Fájlfeltöltésként kell közzétenni multipart/form-data használatával."
}
diff --git a/includes/libs/filebackend/FileOpBatch.php b/includes/libs/filebackend/FileOpBatch.php
index d28cbdfc5359..bcabf47099d7 100644
--- a/includes/libs/filebackend/FileOpBatch.php
+++ b/includes/libs/filebackend/FileOpBatch.php
@@ -64,7 +64,7 @@ class FileOpBatch {
$ignoreErrors = !empty( $opts['force'] );
$maxConcurrency = $opts['concurrency'] ?? 1;
- $predicates = FileOp::newPredicates(); // account for previous ops in prechecks
+ $predicates = new FileStatePredicates(); // account for previous ops in prechecks
$curBatch = []; // concurrent FileOp sub-batch accumulation
$curBatchDeps = FileOp::newDependencies(); // paths used in FileOp sub-batch
$pPerformOps = []; // ordered list of concurrent FileOp sub-batches
diff --git a/includes/libs/filebackend/fileop/CopyFileOp.php b/includes/libs/filebackend/fileop/CopyFileOp.php
index 779fece9490a..cee3e7f7ea18 100644
--- a/includes/libs/filebackend/fileop/CopyFileOp.php
+++ b/includes/libs/filebackend/fileop/CopyFileOp.php
@@ -34,18 +34,19 @@ class CopyFileOp extends FileOp {
];
}
- protected function doPrecheck( array &$predicates ) {
+ protected function doPrecheck(
+ FileStatePredicates $opPredicates,
+ FileStatePredicates $batchPredicates
+ ) {
$status = StatusValue::newGood();
// Check source file existence
- $srcExists = $this->fileExists( $this->params['src'], $predicates );
+ $srcExists = $this->resolveFileExistence( $this->params['src'], $opPredicates );
if ( $srcExists === false ) {
if ( $this->getParam( 'ignoreMissingSource' ) ) {
$this->cancelled = true; // no-op
// Update file existence predicates (cache 404s)
- $predicates[self::ASSUMED_EXISTS][$this->params['src']] = false;
- $predicates[self::ASSUMED_SIZE][$this->params['src']] = false;
- $predicates[self::ASSUMED_SHA1][$this->params['src']] = false;
+ $batchPredicates->assumeFileDoesNotExist( $this->params['src'] );
return $status; // nothing to do
} else {
@@ -58,15 +59,23 @@ class CopyFileOp extends FileOp {
return $status;
}
- // Check if destination file exists
- $status->merge( $this->precheckDestExistence( $predicates ) );
+ // Check if an incompatible destination file exists
+ $srcSize = function () use ( $opPredicates ) {
+ static $size = null;
+ $size ??= $this->resolveFileSize( $this->params['src'], $opPredicates );
+ return $size;
+ };
+ $srcSha1 = function () use ( $opPredicates ) {
+ static $sha1 = null;
+ $sha1 ??= $this->resolveFileSha1Base36( $this->params['src'], $opPredicates );
+ return $sha1;
+ };
+ $status->merge( $this->precheckDestExistence( $opPredicates, $srcSize, $srcSha1 ) );
$this->params['dstExists'] = $this->destExists; // see FileBackendStore::setFileCache()
// Update file existence predicates if the operation is expected to be allowed to run
if ( $status->isOK() ) {
- $predicates[self::ASSUMED_EXISTS][$this->params['dst']] = true;
- $predicates[self::ASSUMED_SIZE][$this->params['dst']] = $this->sourceSize;
- $predicates[self::ASSUMED_SHA1][$this->params['dst']] = $this->sourceSha1;
+ $batchPredicates->assumeFileExists( $this->params['dst'], $srcSize, $srcSha1 );
}
return $status; // safe to call attempt()
diff --git a/includes/libs/filebackend/fileop/CreateFileOp.php b/includes/libs/filebackend/fileop/CreateFileOp.php
index ef825c6318f6..d1897a13a915 100644
--- a/includes/libs/filebackend/fileop/CreateFileOp.php
+++ b/includes/libs/filebackend/fileop/CreateFileOp.php
@@ -32,25 +32,28 @@ class CreateFileOp extends FileOp {
];
}
- protected function doPrecheck( array &$predicates ) {
+ protected function doPrecheck(
+ FileStatePredicates $opPredicates,
+ FileStatePredicates $batchPredicates
+ ) {
$status = StatusValue::newGood();
// Check if the source data is too big
- $maxBytes = $this->backend->maxFileSizeInternal();
- if ( strlen( $this->getParam( 'content' ) ) > $maxBytes ) {
- $status->fatal( 'backend-fail-maxsize', $this->params['dst'], $maxBytes );
+ $sourceSize = $this->getSourceSize();
+ $maxFileSize = $this->backend->maxFileSizeInternal();
+ if ( $sourceSize > $maxFileSize ) {
+ $status->fatal( 'backend-fail-maxsize', $this->params['dst'], $maxFileSize );
return $status;
}
// Check if an incompatible destination file exists
- $status->merge( $this->precheckDestExistence( $predicates ) );
+ $sourceSha1 = $this->getSourceSha1Base36();
+ $status->merge( $this->precheckDestExistence( $opPredicates, $sourceSize, $sourceSha1 ) );
$this->params['dstExists'] = $this->destExists; // see FileBackendStore::setFileCache()
// Update file existence predicates if the operation is expected to be allowed to run
if ( $status->isOK() ) {
- $predicates[self::ASSUMED_EXISTS][$this->params['dst']] = true;
- $predicates[self::ASSUMED_SIZE][$this->params['dst']] = $this->sourceSize;
- $predicates[self::ASSUMED_SHA1][$this->params['dst']] = $this->sourceSha1;
+ $batchPredicates->assumeFileExists( $this->params['dst'], $sourceSize, $sourceSha1 );
}
return $status; // safe to call attempt()
diff --git a/includes/libs/filebackend/fileop/DeleteFileOp.php b/includes/libs/filebackend/fileop/DeleteFileOp.php
index 251b7bbc7419..7c7c39f850e0 100644
--- a/includes/libs/filebackend/fileop/DeleteFileOp.php
+++ b/includes/libs/filebackend/fileop/DeleteFileOp.php
@@ -30,18 +30,19 @@ class DeleteFileOp extends FileOp {
return [ [ 'src' ], [ 'ignoreMissingSource' ], [ 'src' ] ];
}
- protected function doPrecheck( array &$predicates ) {
+ protected function doPrecheck(
+ FileStatePredicates $opPredicates,
+ FileStatePredicates $batchPredicates
+ ) {
$status = StatusValue::newGood();
// Check source file existence
- $srcExists = $this->fileExists( $this->params['src'], $predicates );
+ $srcExists = $this->resolveFileExistence( $this->params['src'], $opPredicates );
if ( $srcExists === false ) {
if ( $this->getParam( 'ignoreMissingSource' ) ) {
$this->cancelled = true; // no-op
// Update file existence predicates (cache 404s)
- $predicates[self::ASSUMED_EXISTS][$this->params['src']] = false;
- $predicates[self::ASSUMED_SIZE][$this->params['src']] = false;
- $predicates[self::ASSUMED_SHA1][$this->params['src']] = false;
+ $batchPredicates->assumeFileDoesNotExist( $this->params['src'] );
return $status; // nothing to do
} else {
@@ -56,9 +57,7 @@ class DeleteFileOp extends FileOp {
}
// Update file existence predicates since the operation is expected to be allowed to run
- $predicates[self::ASSUMED_EXISTS][$this->params['src']] = false;
- $predicates[self::ASSUMED_SIZE][$this->params['src']] = false;
- $predicates[self::ASSUMED_SHA1][$this->params['src']] = false;
+ $batchPredicates->assumeFileDoesNotExist( $this->params['src'] );
return $status; // safe to call attempt()
}
diff --git a/includes/libs/filebackend/fileop/DescribeFileOp.php b/includes/libs/filebackend/fileop/DescribeFileOp.php
index 029c45132a64..70411963e416 100644
--- a/includes/libs/filebackend/fileop/DescribeFileOp.php
+++ b/includes/libs/filebackend/fileop/DescribeFileOp.php
@@ -30,11 +30,14 @@ class DescribeFileOp extends FileOp {
return [ [ 'src' ], [ 'headers' ], [ 'src' ] ];
}
- protected function doPrecheck( array &$predicates ) {
+ protected function doPrecheck(
+ FileStatePredicates $opPredicates,
+ FileStatePredicates $batchPredicates
+ ) {
$status = StatusValue::newGood();
// Check source file existence
- $srcExists = $this->fileExists( $this->params['src'], $predicates );
+ $srcExists = $this->resolveFileExistence( $this->params['src'], $opPredicates );
if ( $srcExists === false ) {
$status->fatal( 'backend-fail-notexists', $this->params['src'] );
@@ -46,11 +49,17 @@ class DescribeFileOp extends FileOp {
}
// Update file existence predicates since the operation is expected to be allowed to run
- $predicates[self::ASSUMED_EXISTS][$this->params['src']] = $srcExists;
- $predicates[self::ASSUMED_SIZE][$this->params['src']] =
- $this->fileSize( $this->params['src'], $predicates );
- $predicates[self::ASSUMED_SHA1][$this->params['src']] =
- $this->fileSha1( $this->params['src'], $predicates );
+ $srcSize = function () use ( $opPredicates ) {
+ static $size = null;
+ $size ??= $this->resolveFileSize( $this->params['src'], $opPredicates );
+ return $size;
+ };
+ $srcSha1 = function () use ( $opPredicates ) {
+ static $sha1 = null;
+ $sha1 ??= $this->resolveFileSha1Base36( $this->params['src'], $opPredicates );
+ return $sha1;
+ };
+ $batchPredicates->assumeFileExists( $this->params['src'], $srcSize, $srcSha1 );
return $status; // safe to call attempt()
}
diff --git a/includes/libs/filebackend/fileop/FileOp.php b/includes/libs/filebackend/fileop/FileOp.php
index 68b4d276c0c7..3bfd362e5e91 100644
--- a/includes/libs/filebackend/fileop/FileOp.php
+++ b/includes/libs/filebackend/fileop/FileOp.php
@@ -52,15 +52,9 @@ abstract class FileOp {
/** @var bool */
protected $cancelled = false;
- /** @var int|bool */
- protected $sourceSize;
- /** @var string|bool */
- protected $sourceSha1;
-
- /** @var bool */
+ /** @var bool|null */
protected $overwriteSameCase;
-
- /** @var bool */
+ /** @var bool|null */
protected $destExists;
/* Object life-cycle */
@@ -68,10 +62,6 @@ abstract class FileOp {
private const STATE_CHECKED = 2;
private const STATE_ATTEMPTED = 3;
- protected const ASSUMED_SHA1 = 'sha1';
- protected const ASSUMED_EXISTS = 'exists';
- protected const ASSUMED_SIZE = 'size';
-
/**
* Build a new batch file operation transaction
*
@@ -142,15 +132,6 @@ abstract class FileOp {
}
/**
- * Get a new empty predicates array for precheck()
- *
- * @return array
- */
- final public static function newPredicates() {
- return [ self::ASSUMED_EXISTS => [], self::ASSUMED_SHA1 => [], self::ASSUMED_SIZE => [] ];
- }
-
- /**
* Get a new empty dependency tracking array for paths read/written to
*
* @return array
@@ -162,7 +143,7 @@ abstract class FileOp {
/**
* Update a dependency tracking array to account for this operation
*
- * @param array $deps Prior path reads/writes; format of FileOp::newPredicates()
+ * @param array $deps Prior path reads/writes; format of FileOp::newDependencies()
* @return array
*/
final public function applyDependencies( array $deps ) {
@@ -175,7 +156,7 @@ abstract class FileOp {
/**
* Check if this operation changes files listed in $paths
*
- * @param array $deps Prior path reads/writes; format of FileOp::newPredicates()
+ * @param array $deps Prior path reads/writes; format of FileOp::newDependencies()
* @return bool
*/
final public function dependsOn( array $deps ) {
@@ -194,14 +175,14 @@ abstract class FileOp {
}
/**
- * Check preconditions of the operation without writing anything.
- * This must update $predicates for each path that the op can change
- * except when a failing StatusValue object is returned.
+ * Do a dry-run precondition check of the operation in the context of op batch
+ *
+ * Updates the batch predicates for all paths this op can change if an OK status is returned
*
- * @param array &$predicates
+ * @param FileStatePredicates $predicates Counterfactual file states for the op batch
* @return StatusValue
*/
- final public function precheck( array &$predicates ) {
+ final public function precheck( FileStatePredicates $predicates ) {
if ( $this->state !== self::STATE_NEW ) {
return StatusValue::newFatal( 'fileop-fail-state', self::STATE_NEW, $this->state );
}
@@ -217,7 +198,8 @@ abstract class FileOp {
return $status;
}
- $status = $this->doPrecheck( $predicates );
+ $opPredicates = $predicates->snapshot( $this->storagePathsReadOrChanged() );
+ $status = $this->doPrecheck( $opPredicates, $predicates );
if ( !$status->isOK() ) {
$this->failed = true;
}
@@ -226,10 +208,18 @@ abstract class FileOp {
}
/**
- * @param array &$predicates
+ * Do a dry-run precondition check of the operation in the context of op batch
+ *
+ * Updates the batch predicates for all paths this op can change if an OK status is returned
+ *
+ * @param FileStatePredicates $opPredicates Counterfactual file states for op paths at op start
+ * @param FileStatePredicates $batchPredicates Counterfactual file states for the op batch
* @return StatusValue
*/
- protected function doPrecheck( array &$predicates ) {
+ protected function doPrecheck(
+ FileStatePredicates $opPredicates,
+ FileStatePredicates $batchPredicates
+ ) {
return StatusValue::newGood();
}
@@ -349,25 +339,24 @@ abstract class FileOp {
}
/**
- * Check for errors with regards to the destination file already existing.
- * Also set destExists, overwriteSameCase, sourceSize, and sourceSha1 member variables.
+ * Check for errors with regards to the destination file already existing
+ *
+ * Also set the destExists and overwriteSameCase member variables.
* A bad StatusValue will be returned if there is no chance it can be overwritten.
*
- * @param array $predicates
+ * @param FileStatePredicates $opPredicates Counterfactual storage path states for this op
+ * @param int|false|Closure $sourceSize Source size or idempotent function yielding the size
+ * @param string|Closure $sourceSha1 Source hash, or, idempotent function yielding the hash
* @return StatusValue
*/
- protected function precheckDestExistence( array $predicates ) {
+ protected function precheckDestExistence(
+ FileStatePredicates $opPredicates,
+ $sourceSize,
+ $sourceSha1
+ ) {
$status = StatusValue::newGood();
- // Record the size of source file/string
- $this->sourceSize = $this->getSourceSize(); // FS file or data string
- // file in storage?
- $this->sourceSize ??= $this->fileSize( $this->params['src'], $predicates );
- // Record the hash of source file/string
- $this->sourceSha1 = $this->getSourceSha1Base36(); // FS file or data string
- // file in storage?
- $this->sourceSha1 ??= $this->fileSha1( $this->params['src'], $predicates );
// Record the existence of destination file
- $this->destExists = $this->fileExists( $this->params['dst'], $predicates );
+ $this->destExists = $this->resolveFileExistence( $this->params['dst'], $opPredicates );
// Check if an incompatible file exists at the destination
$this->overwriteSameCase = false;
if ( $this->destExists ) {
@@ -375,14 +364,16 @@ abstract class FileOp {
return $status; // OK, no conflict
} elseif ( $this->getParam( 'overwriteSame' ) ) {
// Operation does nothing other than return an OK or bad status
- $dhash = $this->fileSha1( $this->params['dst'], $predicates );
- $dsize = $this->fileSize( $this->params['dst'], $predicates );
+ $sourceSize = ( $sourceSize instanceof Closure ) ? $sourceSize() : $sourceSize;
+ $sourceSha1 = ( $sourceSha1 instanceof Closure ) ? $sourceSha1() : $sourceSha1;
+ $dstSha1 = $this->resolveFileSha1Base36( $this->params['dst'], $opPredicates );
+ $dstSize = $this->resolveFileSize( $this->params['dst'], $opPredicates );
// Check if hashes are valid and match each other...
- if ( !strlen( $this->sourceSha1 ) || !strlen( $dhash ) ) {
+ if ( !strlen( $sourceSha1 ) || !strlen( $dstSha1 ) ) {
$status->fatal( 'backend-fail-hashes' );
- } elseif ( !is_int( $this->sourceSize ) || !is_int( $dsize ) ) {
+ } elseif ( !is_int( $sourceSize ) || !is_int( $dstSize ) ) {
$status->fatal( 'backend-fail-sizes' );
- } elseif ( $this->sourceSha1 !== $dhash || $this->sourceSize !== $dsize ) {
+ } elseif ( $sourceSha1 !== $dstSha1 || $sourceSize !== $dstSize ) {
// Give an error if the files are not identical
$status->fatal( 'backend-fail-notsame', $this->params['dst'] );
} else {
@@ -399,43 +390,22 @@ abstract class FileOp {
}
/**
- * precheckDestExistence() helper function to get the source file size.
- * Subclasses should overwrite this if the source is not in storage.
- *
- * @return int|false|null Returns false on failure
- */
- protected function getSourceSize() {
- return null; // N/A
- }
-
- /**
- * precheckDestExistence() helper function to get the source file SHA-1.
- * Subclasses should overwrite this if the source is not in storage.
- *
- * @return string|false|null Returns false on failure
- */
- protected function getSourceSha1Base36() {
- return null; // N/A
- }
-
- /**
* Check if a file will exist in storage when this operation is attempted
*
* Ideally, the file stat entry should already be preloaded via preloadFileStat().
* Otherwise, this will query the backend.
*
* @param string $source Storage path
- * @param array $predicates
+ * @param FileStatePredicates $opPredicates Counterfactual storage path states for this op
* @return bool|null Whether the file will exist or null on error
*/
- final protected function fileExists( $source, array $predicates ) {
- if ( isset( $predicates[self::ASSUMED_EXISTS][$source] ) ) {
- return $predicates[self::ASSUMED_EXISTS][$source]; // previous op assures this
- } else {
- $params = [ 'src' => $source, 'latest' => true ];
-
- return $this->backend->fileExists( $params );
- }
+ final protected function resolveFileExistence( $source, FileStatePredicates $opPredicates ) {
+ return $opPredicates->resolveFileExistence(
+ $source,
+ function ( $path ) {
+ return $this->backend->fileExists( [ 'src' => $path, 'latest' => true ] );
+ }
+ );
}
/**
@@ -446,44 +416,32 @@ abstract class FileOp {
* Get the size of a file in storage when this operation is attempted
*
* @param string $source Storage path
- * @param array $predicates
+ * @param FileStatePredicates $opPredicates Counterfactual storage path states for this op
* @return int|false False on failure
*/
- final protected function fileSize( $source, array $predicates ) {
- if ( isset( $predicates[self::ASSUMED_SIZE][$source] ) ) {
- return $predicates[self::ASSUMED_SIZE][$source]; // previous op assures this
- } elseif (
- isset( $predicates[self::ASSUMED_EXISTS][$source] ) &&
- !$predicates[self::ASSUMED_EXISTS][$source]
- ) {
- return false; // previous op assures this
- } else {
- $params = [ 'src' => $source, 'latest' => true ];
-
- return $this->backend->getFileSize( $params );
- }
+ final protected function resolveFileSize( $source, FileStatePredicates $opPredicates ) {
+ return $opPredicates->resolveFileSize(
+ $source,
+ function ( $path ) {
+ return $this->backend->getFileSize( [ 'src' => $path, 'latest' => true ] );
+ }
+ );
}
/**
* Get the SHA-1 of a file in storage when this operation is attempted
*
* @param string $source Storage path
- * @param array $predicates
- * @return string|bool The SHA-1 hash the file will have or false if non-existent or on error
+ * @param FileStatePredicates $opPredicates Counterfactual storage path states for this op
+ * @return string|false The SHA-1 hash the file will have or false if non-existent or on error
*/
- final protected function fileSha1( $source, array $predicates ) {
- if ( isset( $predicates[self::ASSUMED_SHA1][$source] ) ) {
- return $predicates[self::ASSUMED_SHA1][$source]; // previous op assures this
- } elseif (
- isset( $predicates[self::ASSUMED_EXISTS][$source] ) &&
- !$predicates[self::ASSUMED_EXISTS][$source]
- ) {
- return false; // previous op assures this
- } else {
- $params = [ 'src' => $source, 'latest' => true ];
-
- return $this->backend->getFileSha1Base36( $params );
- }
+ final protected function resolveFileSha1Base36( $source, FileStatePredicates $opPredicates ) {
+ return $opPredicates->resolveFileSha1Base36(
+ $source,
+ function ( $path ) {
+ return $this->backend->getFileSha1Base36( [ 'src' => $path, 'latest' => true ] );
+ }
+ );
}
/**
diff --git a/includes/libs/filebackend/fileop/FileStatePredicates.php b/includes/libs/filebackend/fileop/FileStatePredicates.php
new file mode 100644
index 000000000000..a9a592751a28
--- /dev/null
+++ b/includes/libs/filebackend/fileop/FileStatePredicates.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileBackend
+ */
+
+/**
+ * Helper class for tracking counterfactual file states when pre-checking file operation batches
+ *
+ * The file states are represented with (existence,size,sha1) triples. When combined with the
+ * current state of files in the backend, this can be used to simulate how a batch of operations
+ * would play out as a "dry run". This is used in FileBackend::doOperations() to bail out if any
+ * failure can be predicted before modifying any data. This includes file operation batches where
+ * the same file gets modified by different operations within the batch.
+ *
+ * @internal Only for use within FileBackend
+ */
+class FileStatePredicates {
+ protected const EXISTS = 'exists';
+ protected const SIZE = 'size';
+ protected const SHA1 = 'sha1';
+
+ /** @var array<string,array> Map of (storage path => file state map) */
+ private $fileStateByPath;
+
+ public function __construct() {
+ $this->fileStateByPath = [];
+ }
+
+ /**
+ * Predicate that a file exists at the path
+ *
+ * @param string $path Storage path
+ * @param int|false|Closure $size File size or idempotent function yielding the size
+ * @param string|Closure $sha1Base36 File hash, or, idempotent function yielding the hash
+ */
+ public function assumeFileExists( string $path, $size, $sha1Base36 ) {
+ $this->fileStateByPath[$path] = [
+ self::EXISTS => true,
+ self::SIZE => $size,
+ self::SHA1 => $sha1Base36
+ ];
+ }
+
+ /**
+ * Predicate that no file exists at the path
+ *
+ * @param string $path Storage path
+ */
+ public function assumeFileDoesNotExist( string $path ) {
+ $this->fileStateByPath[$path] = [
+ self::EXISTS => false,
+ self::SIZE => false,
+ self::SHA1 => false
+ ];
+ }
+
+ /**
+ * Get the hypothetical existance a file given predicated and current state of files
+ *
+ * @param string $path Storage path
+ * @param Closure $curExistenceFunc Function to compute the current existence for a given path
+ * @return bool|null Whether the file exists; null on error
+ */
+ public function resolveFileExistence( string $path, $curExistenceFunc ) {
+ return self::resolveFileProperty( $path, self::EXISTS, $curExistenceFunc );
+ }
+
+ /**
+ * Get the hypothetical size of a file given predicated and current state of files
+ *
+ * @param string $path Storage path
+ * @param Closure $curSizeFunc Function to compute the current size for a given path
+ * @return int|false Bytes; false on error
+ */
+ public function resolveFileSize( string $path, $curSizeFunc ) {
+ return self::resolveFileProperty( $path, self::SIZE, $curSizeFunc );
+ }
+
+ /**
+ * Get the hypothetical SHA-1 hash of a file given predicated and current state of files
+ *
+ * @param string $path Storage path
+ * @param Closure $curSha1Func Function to compute the current SHA-1 hash for a given path
+ * @return string|false Base 36 SHA-1 hash; false on error
+ */
+ public function resolveFileSha1Base36( string $path, $curSha1Func ) {
+ return self::resolveFileProperty( $path, self::SHA1, $curSha1Func );
+ }
+
+ /**
+ * @param string $path Storage path
+ * @param string $property One of (self::EXISTS, self::SIZE, self::SHA1)
+ * @param Closure $curValueFunc Function to compute the current value for a given path
+ * @return mixed
+ */
+ private function resolveFileProperty( $path, $property, $curValueFunc ) {
+ if ( isset( $this->fileStateByPath[$path] ) ) {
+ // File is predicated to have a counterfactual state
+ $value = $this->fileStateByPath[$path][$property];
+ if ( $value instanceof Closure ) {
+ $value = $value();
+ $this->fileStateByPath[$path][$property] = $value;
+ }
+ } else {
+ // File is not predicated to have a counterfactual state; use the current state
+ $value = $curValueFunc( $path );
+ }
+
+ return $value;
+ }
+
+ /**
+ * @param string[] $paths List of storage paths
+ * @return self Clone predicates limited to the given paths
+ */
+ public function snapshot( array $paths ) {
+ $snapshot = new self();
+ foreach ( $paths as $path ) {
+ if ( isset( $this->fileStateByPath[$path] ) ) {
+ $snapshot->fileStateByPath[$path] = $this->fileStateByPath[$path];
+ }
+ }
+
+ return $snapshot;
+ }
+}
diff --git a/includes/libs/filebackend/fileop/MoveFileOp.php b/includes/libs/filebackend/fileop/MoveFileOp.php
index 06855777aed9..708574b18393 100644
--- a/includes/libs/filebackend/fileop/MoveFileOp.php
+++ b/includes/libs/filebackend/fileop/MoveFileOp.php
@@ -34,18 +34,19 @@ class MoveFileOp extends FileOp {
];
}
- protected function doPrecheck( array &$predicates ) {
+ protected function doPrecheck(
+ FileStatePredicates $opPredicates,
+ FileStatePredicates $batchPredicates
+ ) {
$status = StatusValue::newGood();
// Check source file existence
- $srcExists = $this->fileExists( $this->params['src'], $predicates );
+ $srcExists = $this->resolveFileExistence( $this->params['src'], $opPredicates );
if ( $srcExists === false ) {
if ( $this->getParam( 'ignoreMissingSource' ) ) {
$this->cancelled = true; // no-op
// Update file existence predicates (cache 404s)
- $predicates[self::ASSUMED_EXISTS][$this->params['src']] = false;
- $predicates[self::ASSUMED_SIZE][$this->params['src']] = false;
- $predicates[self::ASSUMED_SHA1][$this->params['src']] = false;
+ $batchPredicates->assumeFileDoesNotExist( $this->params['src'] );
return $status; // nothing to do
} else {
@@ -59,17 +60,25 @@ class MoveFileOp extends FileOp {
return $status;
}
// Check if an incompatible destination file exists
- $status->merge( $this->precheckDestExistence( $predicates ) );
+ $srcSize = function () use ( $opPredicates ) {
+ static $size = null;
+ $size ??= $this->resolveFileSize( $this->params['src'], $opPredicates );
+ return $size;
+ };
+ $srcSha1 = function () use ( $opPredicates ) {
+ static $sha1 = null;
+ $sha1 ??= $this->resolveFileSha1Base36( $this->params['src'], $opPredicates );
+ return $sha1;
+ };
+ $status->merge( $this->precheckDestExistence( $opPredicates, $srcSize, $srcSha1 ) );
$this->params['dstExists'] = $this->destExists; // see FileBackendStore::setFileCache()
// Update file existence predicates if the operation is expected to be allowed to run
if ( $status->isOK() ) {
- $predicates[self::ASSUMED_EXISTS][$this->params['src']] = false;
- $predicates[self::ASSUMED_SIZE][$this->params['src']] = false;
- $predicates[self::ASSUMED_SHA1][$this->params['src']] = false;
- $predicates[self::ASSUMED_EXISTS][$this->params['dst']] = true;
- $predicates[self::ASSUMED_SIZE][$this->params['dst']] = $this->sourceSize;
- $predicates[self::ASSUMED_SHA1][$this->params['dst']] = $this->sourceSha1;
+ $batchPredicates->assumeFileExists( $this->params['dst'], $srcSize, $srcSha1 );
+ if ( $this->params['src'] !== $this->params['dst'] ) {
+ $batchPredicates->assumeFileDoesNotExist( $this->params['src'] );
+ }
}
return $status; // safe to call attempt()
diff --git a/includes/libs/filebackend/fileop/StoreFileOp.php b/includes/libs/filebackend/fileop/StoreFileOp.php
index 76cc4b6c494e..ef4974ff9c96 100644
--- a/includes/libs/filebackend/fileop/StoreFileOp.php
+++ b/includes/libs/filebackend/fileop/StoreFileOp.php
@@ -36,31 +36,38 @@ class StoreFileOp extends FileOp {
];
}
- protected function doPrecheck( array &$predicates ) {
+ protected function doPrecheck(
+ FileStatePredicates $opPredicates,
+ FileStatePredicates $batchPredicates
+ ) {
$status = StatusValue::newGood();
- // Check if the source file exists in the file system and is not too big
+ // Check if the source file exists in the file system
if ( !is_file( $this->params['src'] ) ) {
$status->fatal( 'backend-fail-notexists', $this->params['src'] );
return $status;
}
// Check if the source file is too big
- $maxBytes = $this->backend->maxFileSizeInternal();
- if ( filesize( $this->params['src'] ) > $maxBytes ) {
- $status->fatal( 'backend-fail-maxsize', $this->params['dst'], $maxBytes );
+ $sourceSize = $this->getSourceSize();
+ $maxFileSize = $this->backend->maxFileSizeInternal();
+ if ( $sourceSize > $maxFileSize ) {
+ $status->fatal( 'backend-fail-maxsize', $this->params['dst'], $maxFileSize );
return $status;
}
// Check if an incompatible destination file exists
- $status->merge( $this->precheckDestExistence( $predicates ) );
+ $sourceSha1 = function () {
+ static $sha1 = null;
+ $sha1 ??= $this->getSourceSha1Base36();
+ return $sha1;
+ };
+ $status->merge( $this->precheckDestExistence( $opPredicates, $sourceSize, $sourceSha1 ) );
$this->params['dstExists'] = $this->destExists; // see FileBackendStore::setFileCache()
// Update file existence predicates if the operation is expected to be allowed to run
if ( $status->isOK() ) {
- $predicates[self::ASSUMED_EXISTS][$this->params['dst']] = true;
- $predicates[self::ASSUMED_SIZE][$this->params['dst']] = $this->sourceSize;
- $predicates[self::ASSUMED_SHA1][$this->params['dst']] = $this->sourceSha1;
+ $batchPredicates->assumeFileExists( $this->params['dst'], $sourceSize, $sourceSha1 );
}
return $status; // safe to call attempt()
diff --git a/includes/libs/objectcache/BagOStuff.php b/includes/libs/objectcache/BagOStuff.php
index 9fe15bc0352e..a684cfde013c 100644
--- a/includes/libs/objectcache/BagOStuff.php
+++ b/includes/libs/objectcache/BagOStuff.php
@@ -26,13 +26,13 @@
* @defgroup Cache Cache
*/
-use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Wikimedia\LightweightObjectStore\ExpirationAwareness;
use Wikimedia\LightweightObjectStore\StorageAwareness;
use Wikimedia\ScopedCallback;
+use Wikimedia\Stats\StatsFactory;
/**
* Class representing a cache/ephemeral data store
@@ -83,7 +83,7 @@ abstract class BagOStuff implements
IStoreKeyEncoder,
LoggerAwareInterface
{
- /** @var StatsdDataFactoryInterface */
+ /** @var StatsFactory */
protected $stats;
/** @var LoggerInterface */
protected $logger;
@@ -161,7 +161,7 @@ abstract class BagOStuff implements
*/
public function __construct( array $params = [] ) {
$this->keyspace = $params['keyspace'] ?? 'local';
- $this->stats = $params['stats'] ?? new NullStatsdDataFactory();
+ $this->stats = $params['stats'] ?? StatsFactory::newNull();
$this->setLogger( $params['logger'] ?? new NullLogger() );
$asyncHandler = $params['asyncHandler'] ?? null;
@@ -780,22 +780,6 @@ abstract class BagOStuff implements
}
/**
- * @param string $key Key generated by BagOStuff::makeKeyInternal
- * @return string A stats prefix to describe this class of key (e.g. "objectcache.file")
- */
- protected function determineKeyPrefixForStats( $key ) {
- // Key came directly from BagOStuff::makeKey() or BagOStuff::makeGlobalKey()
- // and thus has the format of "<scope>:<collection>[:<constant or variable>]..."
- $components = explode( ':', $key, 3 );
- // Handle legacy callers that fail to use the key building methods
- $keygroup = $components[1] ?? 'UNKNOWN';
- $statsGroup = 'objectcache';
-
- // Replace dots because they are special in StatsD (T232907)
- return $statsGroup . '.' . strtr( $keygroup, '.', '_' );
- }
-
- /**
* @internal For testing only
* @return float UNIX timestamp
* @codeCoverageIgnore
diff --git a/includes/libs/objectcache/MediumSpecificBagOStuff.php b/includes/libs/objectcache/MediumSpecificBagOStuff.php
index 3990a44ad6f1..2f8fe483a4b1 100644
--- a/includes/libs/objectcache/MediumSpecificBagOStuff.php
+++ b/includes/libs/objectcache/MediumSpecificBagOStuff.php
@@ -1099,6 +1099,20 @@ abstract class MediumSpecificBagOStuff extends BagOStuff {
}
/**
+ * @param string $key Key generated by BagOStuff::makeKeyInternal
+ * @return string A stats prefix to describe this class of key (e.g. "objectcache.file")
+ */
+ private function determinekeyGroupForStats( $key ): string {
+ // Key came directly from BagOStuff::makeKey() or BagOStuff::makeGlobalKey()
+ // and thus has the format of "<scope>:<collection>[:<constant or variable>]..."
+ $components = explode( ':', $key, 3 );
+ // Handle legacy callers that fail to use the key building methods
+ $keygroup = $components[1] ?? 'UNKNOWN';
+
+ return strtr( $keygroup, '.', '_' );
+ }
+
+ /**
* @param string $op Operation name as a MediumSpecificBagOStuff::METRIC_OP_* constant
* @param array<int,string>|array<string,int[]> $keyInfo Key list, if payload sizes are not
* applicable, otherwise, map of (key => (send payload size, receive payload size)); send
@@ -1119,30 +1133,58 @@ abstract class MediumSpecificBagOStuff extends BagOStuff {
}
// Metric prefix for the cache wrapper and key collection name
- $prefix = $this->determineKeyPrefixForStats( $key );
+ $keygroup = $this->determinekeyGroupForStats( $key );
if ( $op === self::METRIC_OP_GET ) {
// This operation was either a "hit" or "miss" for this key
- $name = "{$prefix}.{$op}_" . ( $rPayloadSize === false ? 'miss_rate' : 'hit_rate' );
+ if ( $rPayloadSize === false ) {
+ $statsdName = "objectcache.{$keygroup}.{$op}_miss_rate";
+ $statsName = "bagostuff_miss_total";
+ } else {
+ $statsdName = "objectcache.{$keygroup}.{$op}_hit_rate";
+ $statsName = "bagostuff_hit_total";
+ }
} else {
// There is no concept of "hit" or "miss" for this operation
- $name = "{$prefix}.{$op}_call_rate";
+ $statsdName = "objectcache.{$keygroup}.{$op}_call_rate";
+ $statsName = "bagostuff_call_total";
}
- $deltasByMetric[$name] = ( $deltasByMetric[$name] ?? 0 ) + 1;
+ $deltasByMetric[$statsdName] = [
+ 'delta' => ( $deltasByMetric[$statsdName]['delta'] ?? 0 ) + 1,
+ 'metric' => $statsName,
+ 'keygroup' => $keygroup,
+ 'operation' => $op,
+ ];
if ( $sPayloadSize > 0 ) {
- $name = "{$prefix}.{$op}_bytes_sent";
- $deltasByMetric[$name] = ( $deltasByMetric[$name] ?? 0 ) + $sPayloadSize;
+ $statsdName = "objectcache.{$keygroup}.{$op}_bytes_sent";
+ $statsName = "bagostuff_bytes_sent_total";
+ $deltasByMetric[$statsdName] = [
+ 'delta' => ( $deltasByMetric[$statsdName]['delta'] ?? 0 ) + $sPayloadSize,
+ 'metric' => $statsName,
+ 'keygroup' => $keygroup,
+ 'operation' => $op,
+ ];
}
if ( $rPayloadSize > 0 ) {
- $name = "{$prefix}.{$op}_bytes_read";
- $deltasByMetric[$name] = ( $deltasByMetric[$name] ?? 0 ) + $rPayloadSize;
+ $statsdName = "objectcache.{$keygroup}.{$op}_bytes_read";
+ $statsName = "bagostuff_bytes_read_total";
+ $deltasByMetric[$statsdName] = [
+ 'delta' => ( $deltasByMetric[$statsdName]['delta'] ?? 0 ) + $rPayloadSize,
+ 'metric' => $statsName,
+ 'keygroup' => $keygroup,
+ 'operation' => $op,
+ ];
}
}
- foreach ( $deltasByMetric as $name => $delta ) {
- $this->stats->updateCount( $name, $delta );
+ foreach ( $deltasByMetric as $statsdName => $delta ) {
+ $this->stats->getCounter( $delta['metric'] )
+ ->setLabel( 'keygroup', $delta['keygroup'] )
+ ->setLabel( 'operation', $delta['operation'] )
+ ->copyToStatsdAt( $statsdName )
+ ->incrementBy( $delta['delta'] );
}
}
}
diff --git a/includes/linker/LinkRenderer.php b/includes/linker/LinkRenderer.php
index acfbcc5bda3b..3c5e26ba3394 100644
--- a/includes/linker/LinkRenderer.php
+++ b/includes/linker/LinkRenderer.php
@@ -22,7 +22,7 @@ namespace MediaWiki\Linker;
use HtmlArmor;
use Language;
-use LinkCache;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\HookContainer\HookRunner;
diff --git a/includes/linker/LinkRendererFactory.php b/includes/linker/LinkRendererFactory.php
index c519030e99c0..e4eb3f284cb3 100644
--- a/includes/linker/LinkRendererFactory.php
+++ b/includes/linker/LinkRendererFactory.php
@@ -20,7 +20,7 @@
*/
namespace MediaWiki\Linker;
-use LinkCache;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\SpecialPage\SpecialPageFactory;
diff --git a/includes/objectcache/ObjectCache.php b/includes/objectcache/ObjectCache.php
index b9551d43e2fa..91ea0a57daee 100644
--- a/includes/objectcache/ObjectCache.php
+++ b/includes/objectcache/ObjectCache.php
@@ -155,7 +155,7 @@ class ObjectCache {
'keyspace' => self::getDefaultKeyspace(),
'asyncHandler' => [ DeferredUpdates::class, 'addCallableUpdate' ],
'reportDupes' => true,
- 'stats' => $services->getStatsdDataFactory(),
+ 'stats' => $services->getStatsFactory(),
];
if ( isset( $params['factory'] ) ) {
diff --git a/includes/page/Article.php b/includes/page/Article.php
index a8044a358e1d..ad97019e2c35 100644
--- a/includes/page/Article.php
+++ b/includes/page/Article.php
@@ -744,6 +744,9 @@ class Article implements Page {
// we already checked in fetchRevisionRecord()
$opt |= ParserOutputAccess::OPT_NO_AUDIENCE_CHECK;
+ // enable stampede protection and allow stale content
+ $opt |= ParserOutputAccess::OPT_FOR_ARTICLE_VIEW;
+
// Attempt to trigger WikiPage::triggerOpportunisticLinksUpdate
// Ideally this should not be the responsibility of the ParserCache to control this.
// See https://phabricator.wikimedia.org/T329842#8816557 for more context.
diff --git a/includes/page/PageSelectQueryBuilder.php b/includes/page/PageSelectQueryBuilder.php
index 8af7d48c9e28..a8bf8cd4d54d 100644
--- a/includes/page/PageSelectQueryBuilder.php
+++ b/includes/page/PageSelectQueryBuilder.php
@@ -3,7 +3,7 @@
namespace MediaWiki\Page;
use Iterator;
-use LinkCache;
+use MediaWiki\Cache\LinkCache;
use Wikimedia\Assert\Assert;
use Wikimedia\Rdbms\IExpression;
use Wikimedia\Rdbms\IReadableDatabase;
diff --git a/includes/page/PageStore.php b/includes/page/PageStore.php
index 0d9d186ac324..2210491ff7ec 100644
--- a/includes/page/PageStore.php
+++ b/includes/page/PageStore.php
@@ -7,8 +7,8 @@ use EmptyIterator;
use IDBAccessObject;
use InvalidArgumentException;
use Iterator;
-use LinkCache;
use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\DAO\WikiAwareEntity;
use MediaWiki\MainConfigNames;
diff --git a/includes/page/PageStoreFactory.php b/includes/page/PageStoreFactory.php
index 17cf2f6384b4..2d7dec5b9ce8 100644
--- a/includes/page/PageStoreFactory.php
+++ b/includes/page/PageStoreFactory.php
@@ -2,8 +2,8 @@
namespace MediaWiki\Page;
-use LinkCache;
use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\DAO\WikiAwareEntity;
use MediaWiki\Title\NamespaceInfo;
diff --git a/includes/page/ParserOutputAccess.php b/includes/page/ParserOutputAccess.php
index b0c40ec49508..d255c83f3aad 100644
--- a/includes/page/ParserOutputAccess.php
+++ b/includes/page/ParserOutputAccess.php
@@ -84,6 +84,20 @@ class ParserOutputAccess {
*/
public const OPT_LINKS_UPDATE = 8;
+ /**
+ * Apply page view semantics. This relaxes some guarantees, specifically:
+ * - Use PoolCounter for stampede protection, causing the request to
+ * block until another process has finished rendering the content.
+ * - Allow stale parser output to be returned to prevent long waits for
+ * slow renders.
+ * - Allow cacheable placeholder output to be returned when PoolCounter
+ * fails to obtain a lock. See the PoolCounterConf setting for details.
+ *
+ * @see Bug T352837
+ * @since 1.42
+ */
+ public const OPT_FOR_ARTICLE_VIEW = 16;
+
/** @var string Do not read or write any cache */
private const CACHE_NONE = 'none';
@@ -302,11 +316,16 @@ class ParserOutputAccess {
}
}
- $work = $this->newPoolWorkArticleView( $page, $parserOptions, $revision, $options );
- /** @var Status $status */
- $status = $work->execute();
+ if ( $options & self::OPT_FOR_ARTICLE_VIEW ) {
+ $work = $this->newPoolWorkArticleView( $page, $parserOptions, $revision, $options );
+ /** @var Status $status */
+ $status = $work->execute();
+ } else {
+ $status = $this->renderRevision( $page, $parserOptions, $revision, $options );
+ }
+
$output = $status->getValue();
- Assert::postcondition( $output || !$status->isOK(), 'Worker returned invalid status' );
+ Assert::postcondition( $output || !$status->isOK(), 'Inconsistent status' );
if ( $output && !$isOld ) {
$primaryCache = $this->getPrimaryCache( $parserOptions );
@@ -326,6 +345,54 @@ class ParserOutputAccess {
}
/**
+ * Render the given revision.
+ *
+ * This method will update the parser cache if appropriate, and will
+ * trigger a links update if OPT_LINKS_UPDATE is set.
+ *
+ * This method does not perform access checks, and will not load content
+ * from caches. The caller is assumed to have taken care of that.
+ *
+ * @see PoolWorkArticleView::renderRevision
+ */
+ private function renderRevision(
+ PageRecord $page,
+ ParserOptions $parserOptions,
+ RevisionRecord $revision,
+ int $options
+ ): Status {
+ $this->statsDataFactory->increment( 'ParserOutputAccess.PoolWork.None' );
+
+ $renderedRev = $this->revisionRenderer->getRenderedRevision(
+ $revision,
+ $parserOptions,
+ null,
+ [ 'audience' => RevisionRecord::RAW ]
+ );
+
+ $output = $renderedRev->getRevisionParserOutput();
+
+ if ( !( $options & self::OPT_NO_UPDATE_CACHE ) && $output->isCacheable() ) {
+ $useCache = $this->shouldUseCache( $page, $revision );
+
+ if ( $useCache === self::CACHE_PRIMARY ) {
+ $primaryCache = $this->getPrimaryCache( $parserOptions );
+ $primaryCache->save( $output, $page, $parserOptions );
+ } elseif ( $useCache === self::CACHE_SECONDARY ) {
+ $secondaryCache = $this->getSecondaryCache( $parserOptions );
+ $secondaryCache->save( $output, $revision, $parserOptions );
+ }
+ }
+
+ if ( $options & self::OPT_LINKS_UPDATE ) {
+ $this->wikiPageFactory->newFromTitle( $page )
+ ->triggerOpportunisticLinksUpdate( $output );
+ }
+
+ return Status::newGood( $output );
+ }
+
+ /**
* @param PageRecord $page
* @param RevisionRecord|null $revision
* @param int $options
@@ -377,7 +444,7 @@ class ParserOutputAccess {
*
* @return PoolCounterWork
*/
- private function newPoolWorkArticleView(
+ protected function newPoolWorkArticleView(
PageRecord $page,
ParserOptions $parserOptions,
RevisionRecord $revision,
diff --git a/includes/parser/DateFormatter.php b/includes/parser/DateFormatter.php
index 097ae5770d47..f46143ea8221 100644
--- a/includes/parser/DateFormatter.php
+++ b/includes/parser/DateFormatter.php
@@ -308,7 +308,7 @@ class DateFormatter {
* year number and 'BC' at the end otherwise.
*/
private function makeNormalYear( $iso ) {
- if ( $iso[0] == '-' ) {
+ if ( $iso <= 0 ) {
$text = ( intval( substr( $iso, 1 ) ) + 1 ) . ' BC';
} else {
$text = intval( $iso );
diff --git a/includes/parser/LinkHolderArray.php b/includes/parser/LinkHolderArray.php
index f3904e41a253..3f552c1c2aac 100644
--- a/includes/parser/LinkHolderArray.php
+++ b/includes/parser/LinkHolderArray.php
@@ -21,6 +21,7 @@
* @ingroup Parser
*/
+use MediaWiki\Cache\LinkCache;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\HookContainer\HookRunner;
use MediaWiki\Linker\Linker;
diff --git a/includes/poolcounter/PoolWorkArticleView.php b/includes/poolcounter/PoolWorkArticleView.php
index 00161f73d128..5fe4beb8a556 100644
--- a/includes/poolcounter/PoolWorkArticleView.php
+++ b/includes/poolcounter/PoolWorkArticleView.php
@@ -21,6 +21,7 @@
namespace MediaWiki\PoolCounter;
use MediaWiki\Logger\Spi as LoggerSpi;
+use MediaWiki\Page\ParserOutputAccess;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Revision\RevisionRenderer;
use MediaWiki\Status\Status;
@@ -73,6 +74,10 @@ class PoolWorkArticleView extends PoolCounterWork {
}
/**
+ * Render the given revision.
+ *
+ * @see ParserOutputAccess::renderRevision
+ *
* @return Status with the value being a ParserOutput or null
*/
public function renderRevision(): Status {
@@ -83,21 +88,7 @@ class PoolWorkArticleView extends PoolCounterWork {
[ 'audience' => RevisionRecord::RAW ]
);
- $time = -microtime( true );
$parserOutput = $renderedRevision->getRevisionParserOutput();
- $time += microtime( true );
-
- // Timing hack
- if ( $time > 3 ) {
- // TODO: Use Parser's logger (once it has one)
- $channel = $this->parserOptions->getUseParsoid() ? 'slow-parsoid' : 'slow-parse';
- $logger = $this->loggerSpi->getLogger( $channel );
- $logger->info( 'Parsing {title} was slow, took {time} seconds', [
- 'time' => number_format( $time, 2 ),
- 'title' => (string)$this->revision->getPageAsLinkTarget(),
- 'trigger' => 'view',
- ] );
- }
return Status::newGood( $parserOutput );
}
diff --git a/includes/revisiondelete/RevDelArchiveList.php b/includes/revisiondelete/RevDelArchiveList.php
index 1f350352a08b..1cbb668a736c 100644
--- a/includes/revisiondelete/RevDelArchiveList.php
+++ b/includes/revisiondelete/RevDelArchiveList.php
@@ -19,6 +19,7 @@
* @ingroup RevisionDelete
*/
+use MediaWiki\Cache\HTMLCacheUpdater;
use MediaWiki\Context\IContextSource;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\MediaWikiServices;
@@ -43,7 +44,7 @@ class RevDelArchiveList extends RevDelRevisionList {
* @param array $ids
* @param LBFactory $lbFactory
* @param HookContainer $hookContainer
- * @param HtmlCacheUpdater $htmlCacheUpdater
+ * @param HTMLCacheUpdater $htmlCacheUpdater
* @param RevisionStore $revisionStore
*/
public function __construct(
@@ -52,7 +53,7 @@ class RevDelArchiveList extends RevDelRevisionList {
array $ids,
LBFactory $lbFactory,
HookContainer $hookContainer,
- HtmlCacheUpdater $htmlCacheUpdater,
+ HTMLCacheUpdater $htmlCacheUpdater,
RevisionStore $revisionStore
) {
parent::__construct(
diff --git a/includes/revisiondelete/RevDelArchivedFileList.php b/includes/revisiondelete/RevDelArchivedFileList.php
index ca9cf8adbe57..7e1138953535 100644
--- a/includes/revisiondelete/RevDelArchivedFileList.php
+++ b/includes/revisiondelete/RevDelArchivedFileList.php
@@ -19,6 +19,7 @@
* @ingroup RevisionDelete
*/
+use MediaWiki\Cache\HTMLCacheUpdater;
use MediaWiki\Context\IContextSource;
use MediaWiki\FileRepo\File\FileSelectQueryBuilder;
use MediaWiki\Page\PageIdentity;
@@ -37,7 +38,7 @@ class RevDelArchivedFileList extends RevDelFileList {
* @param PageIdentity $page
* @param array $ids
* @param LBFactory $lbFactory
- * @param HtmlCacheUpdater $htmlCacheUpdater
+ * @param HTMLCacheUpdater $htmlCacheUpdater
* @param RepoGroup $repoGroup
*/
public function __construct(
@@ -45,7 +46,7 @@ class RevDelArchivedFileList extends RevDelFileList {
PageIdentity $page,
array $ids,
LBFactory $lbFactory,
- HtmlCacheUpdater $htmlCacheUpdater,
+ HTMLCacheUpdater $htmlCacheUpdater,
RepoGroup $repoGroup
) {
parent::__construct(
diff --git a/includes/revisiondelete/RevDelFileList.php b/includes/revisiondelete/RevDelFileList.php
index d80a03650d3a..cdcc328100bf 100644
--- a/includes/revisiondelete/RevDelFileList.php
+++ b/includes/revisiondelete/RevDelFileList.php
@@ -19,6 +19,7 @@
* @ingroup RevisionDelete
*/
+use MediaWiki\Cache\HTMLCacheUpdater;
use MediaWiki\Context\IContextSource;
use MediaWiki\FileRepo\File\FileSelectQueryBuilder;
use MediaWiki\Page\PageIdentity;
@@ -35,7 +36,7 @@ class RevDelFileList extends RevDelList {
protected const SUPPRESS_BIT = File::DELETED_RESTRICTED;
- /** @var HtmlCacheUpdater */
+ /** @var HTMLCacheUpdater */
private $htmlCacheUpdater;
/** @var RepoGroup */
@@ -55,7 +56,7 @@ class RevDelFileList extends RevDelList {
* @param PageIdentity $page
* @param array $ids
* @param LBFactory $lbFactory
- * @param HtmlCacheUpdater $htmlCacheUpdater
+ * @param HTMLCacheUpdater $htmlCacheUpdater
* @param RepoGroup $repoGroup
*/
public function __construct(
@@ -63,7 +64,7 @@ class RevDelFileList extends RevDelList {
PageIdentity $page,
array $ids,
LBFactory $lbFactory,
- HtmlCacheUpdater $htmlCacheUpdater,
+ HTMLCacheUpdater $htmlCacheUpdater,
RepoGroup $repoGroup
) {
parent::__construct( $context, $page, $ids, $lbFactory );
@@ -153,7 +154,7 @@ class RevDelFileList extends RevDelList {
$this->htmlCacheUpdater->purgeUrls(
$purgeUrls,
- HtmlCacheUpdater::PURGE_INTENT_TXROUND_REFLECTED
+ HTMLCacheUpdater::PURGE_INTENT_TXROUND_REFLECTED
);
return Status::newGood();
diff --git a/includes/revisiondelete/RevDelRevisionList.php b/includes/revisiondelete/RevDelRevisionList.php
index 4e4b3cd786fa..38678f97a02f 100644
--- a/includes/revisiondelete/RevDelRevisionList.php
+++ b/includes/revisiondelete/RevDelRevisionList.php
@@ -19,6 +19,7 @@
* @ingroup RevisionDelete
*/
+use MediaWiki\Cache\HTMLCacheUpdater;
use MediaWiki\Context\IContextSource;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\HookContainer\HookRunner;
@@ -49,7 +50,7 @@ class RevDelRevisionList extends RevDelList {
/** @var HookRunner */
private $hookRunner;
- /** @var HtmlCacheUpdater */
+ /** @var HTMLCacheUpdater */
private $htmlCacheUpdater;
/** @var RevisionStore */
@@ -64,7 +65,7 @@ class RevDelRevisionList extends RevDelList {
* @param array $ids
* @param LBFactory $lbFactory
* @param HookContainer $hookContainer
- * @param HtmlCacheUpdater $htmlCacheUpdater
+ * @param HTMLCacheUpdater $htmlCacheUpdater
* @param RevisionStore $revisionStore
*/
public function __construct(
@@ -73,7 +74,7 @@ class RevDelRevisionList extends RevDelList {
array $ids,
LBFactory $lbFactory,
HookContainer $hookContainer,
- HtmlCacheUpdater $htmlCacheUpdater,
+ HTMLCacheUpdater $htmlCacheUpdater,
RevisionStore $revisionStore
) {
parent::__construct( $context, $page, $ids, $lbFactory );
@@ -192,7 +193,7 @@ class RevDelRevisionList extends RevDelList {
public function doPostCommitUpdates( array $visibilityChangeMap ) {
$this->htmlCacheUpdater->purgeTitleUrls(
$this->page,
- HtmlCacheUpdater::PURGE_INTENT_TXROUND_REFLECTED
+ HTMLCacheUpdater::PURGE_INTENT_TXROUND_REFLECTED
);
// Extensions that require referencing previous revisions may need this
$this->hookRunner->onArticleRevisionVisibilitySet(
diff --git a/includes/specials/SpecialEditWatchlist.php b/includes/specials/SpecialEditWatchlist.php
index 961c15835f35..d84d5482c486 100644
--- a/includes/specials/SpecialEditWatchlist.php
+++ b/includes/specials/SpecialEditWatchlist.php
@@ -30,8 +30,8 @@ namespace MediaWiki\Specials;
use EditWatchlistCheckboxSeriesField;
use EditWatchlistNormalHTMLForm;
-use GenderCache;
use LogicException;
+use MediaWiki\Cache\GenderCache;
use MediaWiki\Cache\LinkBatchFactory;
use MediaWiki\Deferred\DeferredUpdates;
use MediaWiki\Html\Html;
diff --git a/includes/specials/SpecialListFiles.php b/includes/specials/SpecialListFiles.php
index ed398cd1a072..f412135b54f2 100644
--- a/includes/specials/SpecialListFiles.php
+++ b/includes/specials/SpecialListFiles.php
@@ -23,6 +23,7 @@
namespace MediaWiki\Specials;
+use MediaWiki\Cache\UserCache;
use MediaWiki\CommentFormatter\CommentFormatter;
use MediaWiki\CommentStore\CommentStore;
use MediaWiki\Pager\ImageListPager;
@@ -31,7 +32,6 @@ use MediaWiki\User\UserNamePrefixSearch;
use MediaWiki\User\UserNameUtils;
use MediaWiki\User\UserRigorOptions;
use RepoGroup;
-use UserCache;
use Wikimedia\Rdbms\IConnectionProvider;
class SpecialListFiles extends IncludableSpecialPage {
diff --git a/includes/specials/SpecialPrefixIndex.php b/includes/specials/SpecialPrefixIndex.php
index 763b756f31da..269e1bf858bd 100644
--- a/includes/specials/SpecialPrefixIndex.php
+++ b/includes/specials/SpecialPrefixIndex.php
@@ -24,7 +24,7 @@
namespace MediaWiki\Specials;
use HTMLCheckField;
-use LinkCache;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Html\Html;
use MediaWiki\HTMLForm\HTMLForm;
use MediaWiki\Title\Title;
diff --git a/includes/specials/SpecialProtectedPages.php b/includes/specials/SpecialProtectedPages.php
index 94fb8e1f985c..f3ad627e82b3 100644
--- a/includes/specials/SpecialProtectedPages.php
+++ b/includes/specials/SpecialProtectedPages.php
@@ -27,6 +27,7 @@ use HTMLMultiSelectField;
use HTMLSelectNamespace;
use HTMLSizeFilterField;
use MediaWiki\Cache\LinkBatchFactory;
+use MediaWiki\Cache\UserCache;
use MediaWiki\CommentFormatter\RowCommentFormatter;
use MediaWiki\CommentStore\CommentStore;
use MediaWiki\HTMLForm\HTMLForm;
@@ -34,7 +35,6 @@ use MediaWiki\MainConfigNames;
use MediaWiki\Pager\ProtectedPagesPager;
use MediaWiki\Permissions\RestrictionStore;
use MediaWiki\SpecialPage\SpecialPage;
-use UserCache;
use Wikimedia\Rdbms\IConnectionProvider;
/**
diff --git a/includes/specials/SpecialUndelete.php b/includes/specials/SpecialUndelete.php
index 2c477d6ebbab..310b3de1f5cc 100644
--- a/includes/specials/SpecialUndelete.php
+++ b/includes/specials/SpecialUndelete.php
@@ -29,10 +29,10 @@ use ChangeTags;
use ErrorPageError;
use File;
use IDBAccessObject;
-use LinkBatch;
use LocalRepo;
use LogEventsList;
use LogPage;
+use MediaWiki\Cache\LinkBatch;
use MediaWiki\Cache\LinkBatchFactory;
use MediaWiki\CommentFormatter\CommentFormatter;
use MediaWiki\CommentStore\CommentStore;
diff --git a/includes/specials/SpecialUploadStash.php b/includes/specials/SpecialUploadStash.php
index 96680153b84a..b785ecacd1ad 100644
--- a/includes/specials/SpecialUploadStash.php
+++ b/includes/specials/SpecialUploadStash.php
@@ -181,6 +181,10 @@ class SpecialUploadStash extends UnlistedSpecialPage {
$handler = $file->getHandler();
if ( $handler ) {
$params = $handler->parseParamString( $paramString );
+ if ( $params === false ) {
+ // The params are invalid, but still try to show a thumb
+ $params = [];
+ }
return [ 'file' => $file, 'type' => $type, 'params' => $params ];
} else {
diff --git a/includes/specials/pagers/ImageListPager.php b/includes/specials/pagers/ImageListPager.php
index 4004ff011e13..7b024e3ef3d8 100644
--- a/includes/specials/pagers/ImageListPager.php
+++ b/includes/specials/pagers/ImageListPager.php
@@ -22,6 +22,7 @@
namespace MediaWiki\Pager;
use LocalRepo;
+use MediaWiki\Cache\UserCache;
use MediaWiki\CommentFormatter\CommentFormatter;
use MediaWiki\CommentStore\CommentStore;
use MediaWiki\Context\IContextSource;
@@ -35,7 +36,6 @@ use MediaWiki\User\User;
use MediaWiki\User\UserNameUtils;
use RepoGroup;
use UnexpectedValueException;
-use UserCache;
use Wikimedia\Rdbms\FakeResultWrapper;
use Wikimedia\Rdbms\IConnectionProvider;
use Wikimedia\Rdbms\IResultWrapper;
diff --git a/includes/specials/pagers/ProtectedPagesPager.php b/includes/specials/pagers/ProtectedPagesPager.php
index a0b40606e394..d4203c8b9c9b 100644
--- a/includes/specials/pagers/ProtectedPagesPager.php
+++ b/includes/specials/pagers/ProtectedPagesPager.php
@@ -24,6 +24,7 @@ namespace MediaWiki\Pager;
use LogEventsList;
use LogPage;
use MediaWiki\Cache\LinkBatchFactory;
+use MediaWiki\Cache\UserCache;
use MediaWiki\CommentFormatter\RowCommentFormatter;
use MediaWiki\CommentStore\CommentStore;
use MediaWiki\Context\IContextSource;
@@ -32,7 +33,6 @@ use MediaWiki\Linker\Linker;
use MediaWiki\Linker\LinkRenderer;
use MediaWiki\Title\Title;
use UnexpectedValueException;
-use UserCache;
use Wikimedia\Rdbms\FakeResultWrapper;
use Wikimedia\Rdbms\IConnectionProvider;
diff --git a/includes/title/MediaWikiTitleCodec.php b/includes/title/MediaWikiTitleCodec.php
index 4ae6585e9c14..1d919d4e79ff 100644
--- a/includes/title/MediaWikiTitleCodec.php
+++ b/includes/title/MediaWikiTitleCodec.php
@@ -23,10 +23,10 @@
namespace MediaWiki\Title;
-use GenderCache;
use InvalidArgumentException;
use Language;
use LogicException;
+use MediaWiki\Cache\GenderCache;
use MediaWiki\Interwiki\InterwikiLookup;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\Message\Message;
diff --git a/includes/title/Title.php b/includes/title/Title.php
index 80165a8b52e6..0ea374bbe577 100644
--- a/includes/title/Title.php
+++ b/includes/title/Title.php
@@ -30,8 +30,8 @@ use IDBAccessObject;
use ILanguageConverter;
use InvalidArgumentException;
use Language;
-use LinkCache;
use MapCacheLRU;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Context\RequestContext;
use MediaWiki\DAO\WikiAwareEntityTrait;
use MediaWiki\Deferred\AtomicSectionUpdate;
@@ -2951,7 +2951,7 @@ class Title implements LinkTarget, PageIdentity {
/**
* Get a list of URLs to purge from the CDN cache when this page changes.
*
- * @deprecated since 1.35 Use HtmlCacheUpdater; hard-deprecated in 1.42
+ * @deprecated since 1.35 Use HTMLCacheUpdater; hard-deprecated in 1.42
* @return string[]
*/
public function getCdnUrls() {
@@ -2962,7 +2962,7 @@ class Title implements LinkTarget, PageIdentity {
/**
* Purge all applicable CDN URLs
- * @deprecated since 1.35 Use HtmlCacheUpdater; hard-deprecated in 1.42
+ * @deprecated since 1.35 Use HTMLCacheUpdater; hard-deprecated in 1.42
*/
public function purgeSquid() {
wfDeprecated( __METHOD__, '1.35' );
diff --git a/includes/upload/UploadFromUrl.php b/includes/upload/UploadFromUrl.php
index c5eaf8f820f3..8753913e331e 100644
--- a/includes/upload/UploadFromUrl.php
+++ b/includes/upload/UploadFromUrl.php
@@ -117,6 +117,26 @@ class UploadFromUrl extends UploadBase {
}
/**
+ * Provides a caching key for an upload from url set of parameters
+ * Used to set the status of an async job in UploadFromUrlJob
+ * and retreive it in frontend clients like ApiUpload. Will return the
+ * empty string if not all parameters are present.
+ *
+ * @param array $params
+ * @return string
+ */
+ public static function getCacheKey( $params ) {
+ if ( !isset( $params['filename'] ) || !isset( $params['url'] ) ) {
+ return "";
+ } else {
+ // We use sha1 here to ensure we have a fixed-length string of printable
+ // characters. There is no cryptography involved, so we just need a
+ // relatively fast function.
+ return sha1( sprintf( "%s|||%s", $params['filename'], $params['url'] ) );
+ }
+ }
+
+ /**
* @return string[]
*/
private static function getAllowedHosts(): array {
diff --git a/includes/user/User.php b/includes/user/User.php
index a891bc7877f1..b65852ae877e 100644
--- a/includes/user/User.php
+++ b/includes/user/User.php
@@ -33,6 +33,7 @@ use MediaWiki\Auth\AuthManager;
use MediaWiki\Block\AbstractBlock;
use MediaWiki\Block\Block;
use MediaWiki\Block\SystemBlock;
+use MediaWiki\Cache\UserCache;
use MediaWiki\Context\RequestContext;
use MediaWiki\DAO\WikiAwareEntityTrait;
use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
@@ -59,7 +60,6 @@ use PasswordFactory;
use RuntimeException;
use stdClass;
use UnexpectedValueException;
-use UserCache;
use UserPasswordPolicy;
use WANObjectCache;
use Wikimedia\Assert\Assert;
diff --git a/jsdoc.js b/jsdoc.js
index 32284bc817b9..112277a02f58 100644
--- a/jsdoc.js
+++ b/jsdoc.js
@@ -22,12 +22,7 @@ module.exports = {
got round to reviewing them and incorporating them into the documentation page yet. */
'resources/src/codex',
'resources/src/codex-search',
- 'resources/src/mediawiki.ForeignStructuredUpload.BookletLayout',
- 'resources/src/mediawiki.rcfilters',
'resources/src/mediawiki.router',
- 'resources/src/mediawiki.Upload.BookletLayout',
- 'resources/src/mediawiki.Upload.Dialog.js',
- 'resources/src/mediawiki.htmlform.ooui',
'resources/src/mediawiki.language.specialCharacters',
'resources/src/moment',
'resources/src/oojs-global.js',
@@ -57,22 +52,39 @@ module.exports = {
'jQuery.Event': 'https://api.jquery.com/Types/#Event',
'jQuery.Promise': 'https://api.jquery.com/Types/#Promise',
Node: 'https://developer.mozilla.org/en-US/docs/Web/API/Node',
+ 'OO.ui.ActionFieldLayout': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.ActionFieldLayout.html',
+ 'OO.ui.ButtonGroupWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.ButtonGroupWidget.html',
+ 'OO.ui.ButtonOptionWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.ButtonOptionWidget.html',
+ 'OO.ui.ButtonMenuSelectWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.ButtonMenuSelectWidget.html',
+ 'OO.ui.BookletLayout': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.BookletLayout.html',
'OO.ui.ButtonWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.ButtonWidget.html',
+ 'OO.ui.CheckboxInputWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.CheckboxInputWidget',
'OO.ui.CopyTextLayout': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.CopyTextLayout.html',
'OO.ui.DropdownInputWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.DropdownInputWidget.html',
+ 'OO.ui.DropdownWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.OO.ui.DropdownWidget.html',
+ 'OO.ui.FieldLayout': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.FieldLayout.html',
+ 'OO.ui.FormLayout': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.FormLayout.html',
'OO.ui.InputWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.InputWidget.html',
'OO.ui.MenuOptionWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.MenuOptionWidget.html',
+ 'OO.ui.MenuSectionOptionWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.MenuSectionOptionWidget.html',
+ 'OO.ui.MenuSelectWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.MenuSelectWidget.html',
'OO.ui.MenuTagMultiselectWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.MenuTagMultiselectWidget.html',
'OO.ui.MessageDialog': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.MessageDialog.html',
'OO.ui.OptionWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.OptionWidget.html',
+ 'OO.ui.PopupButtonWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.PopupButtonWidget.html',
+ 'OO.ui.PopupWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.PopupWidget.html',
+ 'OO.ui.PageLayout': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.PageLayout.html',
'OO.ui.ProcessDialog': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.ProcessDialog.html',
'OO.ui.SearchWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.SearchWidget.html',
'OO.ui.SearchInputWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.SearchInputWidget.html',
+ 'OO.ui.SelectFileInputWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.SelectFileInputWidget.html',
'OO.ui.TagItemWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.TagItemWidget.html',
'OO.ui.TagMultiselectWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.TagMultiselectWidget.html',
'OO.ui.TextInputWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.TextInputWidget.html',
+ 'OO.ui.ToggleButtonWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.ToggleButtonWidget.html',
'OO.ui.ToggleSwitchWidget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.ToggleSwitchWidget.html',
'OO.ui.Widget': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.Widget.html',
+ 'OO.ui.WindowManager': 'https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.WindowManager.html',
Promise: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise',
Set: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set',
URLSearchParams: 'https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams'
diff --git a/languages/i18n/acm.json b/languages/i18n/acm.json
index 1a99e5b42928..2f79c270ddf1 100644
--- a/languages/i18n/acm.json
+++ b/languages/i18n/acm.json
@@ -1752,6 +1752,7 @@
"backend-fail-connect": "تعذر ربط الإتصال بخلفية التخزين \"$1\".",
"backend-fail-internal": "صار خطأ مو معروف في خلفية التخزين \"$1\".",
"backend-fail-contenttype": "تعذر تحديد نوع محتوى الملف اللي تريد تخزنه في \"$1\".",
+ "lockmanager-notlocked": "ما انفتح \"$1\"؛ هسه هوه مو مفتوح.",
"zip-file-open-error": "صار خطأ أثناء فتح الملف لفحوصات ZIP.",
"zip-wrong-format": "الملف المحدد مو ملف ZIP.",
"uploadstash-clear": "مسح الملفات المضمومة",
@@ -1759,6 +1760,7 @@
"uploadstash-errclear": "فشلت عملية مسح الملفات.",
"uploadstash-refresh": "تحديث قائمة الملفات",
"uploadstash-header-date": "التاريخ",
+ "uploadstash-header-dimensions": "أبعاد",
"uploadstash-bad-path": "المسار مو موجود.",
"uploadstash-bad-path-invalid": "المسار مو صحيح.",
"uploadstash-bad-path-unknown-type": "نوع مو معروف \"$1\".",
@@ -1831,8 +1833,16 @@
"filedelete-reason-otherlist": "سبب ثاني",
"filedelete-edit-reasonlist": "عدل أسباب الحذف",
"filedelete-edit-reasonlist-suppress": "عدل أسباب الإخفاء",
+ "filedelete-maintenance-title": "ما يكدر يحذف الملف",
"randompage": "صفحة عشوائية",
+ "randomincategory-submit": "امشي",
"statistics": "احصائيات",
+ "statistics-header-pages": "إحصائيات الصفحات",
+ "statistics-header-edits": "إحصائيات التعديلات",
+ "statistics-header-users": "إحصائيات المستخدمين",
+ "statistics-header-hooks": "إحصائيات ثانية",
+ "statistics-pages": "الصفحات",
+ "pageswithprop-submit": "امشي",
"nmembers": "{{PLURAL:$1|$1 عضو|$1 أعضاء}}",
"prefixindex": "كل الصفحات بالبادئة",
"listusers": "قائمة الأعضاء",
@@ -1841,6 +1851,8 @@
"move": "حرك",
"pager-newer-n": "{{PLURAL:$1|أحدث 1|أحدث $1}}",
"pager-older-n": "{{PLURAL:$1|أقدم 1|أقدم $1}}",
+ "apisandbox-continue": "كمل",
+ "apisandbox-continue-clear": "فرغ",
"booksources": "مصادر كتاب",
"booksources-search-legend": "دور بمصادر الكتب",
"booksources-search": "بحث",
@@ -1850,18 +1862,33 @@
"all-logs-page": "كل السجلات العامة",
"alllogstext": "عرض شامل لكل السجلات المتوفرة في {{SITENAME}}.\nتكدر تخلي القائمة تحديدية أكثر وذلك باختيار نوع السجل واسم المستخدم (حساس لحالة الحروف)، لو الصفحة المتأثرة (همين حساس لحالة الحروف).",
"logempty": "ماكو مدخلات مطابقة بالسجل.",
+ "checkbox-all": "كلشي",
"checkbox-none": "ماكو شي",
+ "checkbox-invert": "عكس",
"allpages": "كل الصفحات",
+ "nextpage": "الصفحة التالية ($1)",
+ "prevpage": "الصفحة السابقة ($1)",
"allarticles": "كل الصفحات",
"allpagessubmit": "امشي",
"allpages-bad-ns": "{{SITENAME}} ما بيها نطاق \"$1\".",
"allpages-hide-redirects": "ضم التحويلات",
"categories": "تصنيفات",
+ "sp-deletedcontributions-contribs": "مساهمات",
+ "linksearch-ok": "بحث",
"listgrouprights-members": "(قائمة الأعضاء)",
"emailuser": "دز إيميل للمستخدم",
"noemailtitle": "ماكو عنوان إيميل",
"nowikiemailtext": "هاذ المستخدم اختار ما يستقبل البريد الإلكتروني من المستخدمين الثانيين.",
"emailusername": "اسم المستخدم:",
+ "emailfrom": "من:",
+ "emailto": "ل:",
+ "emailsubject": "موضوع:",
+ "emailmessage": "رساله:",
+ "emailsend": "دز",
+ "emailccme": "دزلي نسخة من رسالتي.",
+ "emailccsubject": "نسخة من رسالتك لـ $1: $2",
+ "emailsent": "اندز البريد",
+ "emailsenttext": "اندز البريد الالكتروني مالك",
"watchlist": "قائمة المراقبة",
"mywatchlist": "قائمة المراقبة",
"watchlistfor2": "لـ$1",
@@ -1869,13 +1896,35 @@
"watchnologin": "ما مسجل دخول",
"watchlistnotwatchable": "ما تكدر تشوف الصفحة",
"watch": "راقبها",
+ "watchthispage": "راقب هاي الصفحة",
"unwatch": "لا تراقبها",
+ "unwatchthispage": "لا تراقب",
"notanarticle": "مو صفحة محتوى",
"watchlist-details": "{{PLURAL:$1||صفحة وحده|صفحتين|$1 صفحات|$1 صفحة}} في قائمة مراقبتك (بالإضافة لصفحات النقاش).",
"wlheader-showupdated": "الصفحات اللي صار تحريرها ورا مطالعتها آخر مرة موجودة <strong>بالخط الغليط</strong>",
"wlnote": "جوا {{PLURAL:$1|ماكو تغييرات|التغيير الأخير|آخر تغييرين|آخر <strong>$1</strong> تغييرات|آخر <strong>$1</strong> تغيير}} في {{PLURAL:$2||<strong>الساعة</strong> الماضية|<strong>الساعتين</strong> الماضيتين|ال<strong>$2</strong> ساعات الماضية|ال<strong>$2</strong> ساعة الماضية}} وفقاً ل$3، $4.",
+ "wlshowhideminor": "تعديلات طفيفه",
+ "wlshowhidebots": "بوتات",
+ "wlshowhideliu": "مستخدمين مسجلين",
+ "wlshowhideanons": "مستخدمين غريبين",
+ "wlshowhidepatr": "التعديلات المراجعة",
+ "wlshowhidemine": "تعديلاتي",
+ "wlshowhidecategorization": "تصنيف الصفحات",
"watchlist-options": "خيارات قائمة المراقبة",
+ "watching": "يراقب...",
+ "unwatching": "يوخر المراقبة...",
"enotif_reset": "علم كل الصفحات حتى لو جنت زرتها",
+ "enotif_impersonal_salutation": "مستخدم {{SITENAME}}",
+ "enotif_minoredit": "هاذ تعديل طفيف",
+ "deletecomment": "سبب:",
+ "deleteotherreason": "سبب ثاني/اضافي:",
+ "deletereasonotherlist": "سبب ثاني",
+ "deletereason-dropdown": "*أسباب الحذف المعروفة\n** سبام\n** تخريب\n** خرق لحقوق التأليف والنشر\n** طلب المؤلف\n** تحويلة مكسورة",
+ "delete-edit-reasonlist": "عدل أسباب الحذف",
+ "rollback": "رجع التعديلات",
+ "rollback-confirmation-confirm": "رجاءا تأكد:",
+ "rollback-confirmation-yes": "استرجاع",
+ "rollback-confirmation-no": "ألغي",
"rollbacklink": "ارجع",
"rollbacklinkcount": "استرجع {{PLURAL:$1|$1 تعديل|$1 تعديلات}}",
"protect-default": "اسمح لكل المستخدمين",
@@ -1925,6 +1974,7 @@
"movereason": "السـبب:",
"delete_and_move_confirm": "اي، احذف الصفحة",
"export": "تصدير صفحات",
+ "allmessages-filter-translate": "ترجم",
"tooltip-preview": "طلع تغييراتك. من فضلك استخدم هاذ قبل النَّشر.",
"pageinfo-toolboxlink": "معلومات الصفحه",
"pageinfo-contentpage-yes": "اي",
diff --git a/languages/i18n/alt.json b/languages/i18n/alt.json
index 79d14de1e8dc..5142cc9bbbda 100644
--- a/languages/i18n/alt.json
+++ b/languages/i18n/alt.json
@@ -63,6 +63,7 @@
"tog-requireemail": "Јажыт сӧсти тӱжӱре чачатан самараны јӱк ле электрон почта ла туружаачыныҥ ады бичилген кийнинде аткарар",
"tog-forcesafemode": "[[mw:Manual:Safemode|Каршу болдыртпас режимди]] јаантайынга иштедер",
"tog-editrecovery": "[[Special:EditRecovery|{{int:editrecovery}}]] функцияны иштедип ийер",
+ "tog-editrecovery-help": "[$1 проекттиҥ бӱгинде] слер јетирӱ артызар аргалу.",
"underline-always": "Јаантайын",
"underline-never": "Качан да эмес",
"underline-default": "Браузердиҥ текши јазалталары аайынча",
@@ -628,8 +629,10 @@
"edit-recovery-special-recovered-on": "алган јери $1",
"edit-recovery-special-recovered-on-tooltip": "Кӧргӱзӱлӱлерди орныктырарга калганчы катап артыскан ӧйдиҥ кӱни ле ӧйи.",
"edit-recovery-loaded-title": "Тӱзедӱлер орныктырылды",
- "edit-recovery-loaded-message": "Слердиҥ корулалбаган тӱзедӱлеригер автоматически орныктырылган",
- "edit-recovery-loaded-message-different-rev": "Бӱкти тӱзедип баштаган кийнинеҥ бери ол солынып калган болор аргалу деп ајарту эдер Солынтылар эдер алдында ширтеп ийер",
+ "edit-recovery-loaded-message": "Слердиҥ артыспаган солытылар автоматикалык орныктырылган.",
+ "edit-recovery-loaded-message-different-rev": "<em>Бӱкти тӱзедип баштаган кийнинеҥ бери ол солынып калган болор аргалу деп ајарту эдер Солынтылар эдер алдында ширтеп ийер</em>",
+ "edit-recovery-loaded-message-different-rev-publish": "<em>Публикация эдердеҥ озо солынтыгарды ширтегер.</em>",
+ "edit-recovery-loaded-message-different-rev-save": "<em>Артызардаҥ озо эткен солынтыгарды ширтегер.</em>",
"edit-recovery-loaded-show": "Солынтыларды кӧргӱзер",
"edit-recovery-loaded-discard": "Солынтыларды јоголтор",
"summary": "Солынтылардыҥ јартамалы:",
@@ -1087,6 +1090,7 @@
"prefs-searchmisc": "Текши",
"searchprefs": "Јазалталарда бедреш",
"searchprefs-noresults": "Турулталар јок",
+ "searchprefs-results": "$1 {{PLURAL:$1|турулта}}",
"saveprefs": "Артызар",
"restoreprefs": "Озо болгон јазалталарга орныктырар",
"prefs-editing": "Тӱзедиш",
@@ -1875,6 +1879,12 @@
"uploadstash-errclear": "Файлдар арчылбады.",
"uploadstash-refresh": "Файлдардыҥ тизимин јаҥыртар",
"uploadstash-exception": "$1,$2 деп удурум кӧмзӧлӧргӧ кожулганы салынбады.",
+ "uploadstash-nothumb": "Миниатюразы јок",
+ "uploadstash-header-date": "Ӧйи",
+ "uploadstash-header-filekey": "Тӱлкӱӱр",
+ "uploadstash-header-thumb": "Миниатюразы",
+ "uploadstash-header-dimensions": "Кеми",
+ "uploadstash-pager-submit": "Файлдарлу тизимди кӧргӱзер",
"uploadstash-bad-path": "Јолы јок.",
"uploadstash-bad-path-invalid": "Јастыра јол.",
"uploadstash-bad-path-unknown-type": "Јарты јок \"$1\" тип.",
diff --git a/languages/i18n/av.json b/languages/i18n/av.json
index 1077a22450e6..95673e930646 100644
--- a/languages/i18n/av.json
+++ b/languages/i18n/av.json
@@ -388,6 +388,7 @@
"blockedtitle": "ХӀалтӀизабулчи блокалда вуго",
"blockednoreason": "гӀилла бихьизабун гьечӀо",
"loginreqlink": "Жанире лӀугьине",
+ "accmailtitle": "Парол битӀана",
"newarticle": "(ЦӀияб)",
"newarticletext": "РегӀелаздасан нуж лъугьана жеги гӀуцӀун гьечӀеб гьумералде.\nГьеб гӀуцӀизе бокьани, гъоркьехун текст хъвай (цӀикӀкӀун лъазе ралагье. [$1 кумекалъулаб гьумералде]).\nНуж гьанире гъалатӀ ккун щварал ратани, дурго браузералъул цохӀо '''нахъе''' абураб клависалда цуй.",
"noarticletext": "Гьаб гьумералда жеги кинабгӀаги текст гьечӀо.\nДуца бегьула цогидал гьумеразда [[Special:Search/{{PAGENAME}}|гьаб гьумералъул цӀар хъирщизе]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} гьелда хурхарал хъвай-хъвагӀаял цӀехезе],\nяги '''[{{fullurl:{{FULLPAGENAME}}|action=edit}} гьединаб гьумер дуцаго гӀуцӀизе]'''</span>.",
@@ -516,7 +517,7 @@
"prefs-searchoptions": "Балагьизе",
"prefs-files": "Файлал",
"youremail": "Email:",
- "prefs-memberingroups": "{{PLURAL:$1|Групалда|Групалда}} {{GENDER:$2|гъорлӀ}}:",
+ "prefs-memberingroups": "{{GENDER:$2|ГъорлӀ|ГъорлӀ}} {{PLURAL:$1|1=групалда|групалда}}:",
"yourlanguage": "МацӀ:",
"yournick": "ЦӀияб гъоркьхъвай:",
"email": "Электронияб адрес",
diff --git a/languages/i18n/ba.json b/languages/i18n/ba.json
index 4604d6c35b40..46bf2c5bb7c1 100644
--- a/languages/i18n/ba.json
+++ b/languages/i18n/ba.json
@@ -66,7 +66,7 @@
"tog-oldsig": "Ғәмәлдәге имзағыҙ:",
"tog-fancysig": "Имзаның шәхси вики-билгеһе (һеҙҙең ҡатнашыусы битенә автоматик һылтанмаһыҙ)",
"tog-uselivepreview": "Битте яңыртып тормайынса, ҡарап сығыуҙы ҡулланырға",
- "tog-forceeditsummary": "Төҙәтеүҙе тасуирлау (йәки программа көйләүе буйынса кире ҡайтарыу) юлы тултырылмаһа, иҫкәртергә",
+ "tog-forceeditsummary": "Үҙгәртеү аңлатмаһы (йәки кире ҡайтарыу тасуирламаһы) юлы тултырылмаһа мине иҫкәртергә",
"tog-watchlisthideown": "Үҙгәртеүҙәремде күҙәтеү исемлегенән йәшерергә",
"tog-watchlisthidebots": "Боттар үҙгәртеүҙәрен күҙәтеү исемлегенән йәшерергә",
"tog-watchlisthideminor": "Әһәмиәте әҙ булған үҙгәртеүҙәрҙе күҙәтеү исемлегенән йәшерергә",
@@ -1094,7 +1094,7 @@
"prefs-watchlist": "Күҙәтеү исемлеге",
"prefs-editwatchlist": "Күҙәтеү исемлеген үҙгәртеү",
"prefs-editwatchlist-label": "Күҙәтеү исемлегем яҙмаларын үҙгәртеү:",
- "prefs-editwatchlist-edit": "Күҙәтеү исемлегегеҙҙән мәҡәлә исемдәрен ҡарағыҙ һәм юйығыҙ",
+ "prefs-editwatchlist-edit": "Күҙәтеү исемлегегеҙҙәге атамаларҙы ҡарау һәм юйыу",
"prefs-editwatchlist-raw": "Күҙәтеү исемлеген текст һымаҡ үҙгәртеү",
"prefs-editwatchlist-clear": "Күҙәтеүҙәр исемлегегеҙҙе таҙартығыҙ",
"prefs-watchlist-days": "Күҙәтеү исемлегендә күрһәтелгән көндәр:",
@@ -1155,7 +1155,7 @@
"timezoneregion-indian": "Һинд океаны",
"timezoneregion-pacific": "Тымыҡ океан",
"allowemail": "Башҡа ҡатнашыусыларға миңә электрон хат яҙырға рөхсәт итергә",
- "email-allow-new-users-label": "Яңы ғына ингән ҡатнашыусыларҙан электрон хат алыуҙы рөхсәт итергә",
+ "email-allow-new-users-label": "Яңы ҡатнашыусыларҙан электрон хат алыуҙы рөхсәт итергә",
"email-mutelist-label": "Был ҡатнашыусыларға миңә хат ебәреүҙе тыйырға:",
"prefs-searchoptions": "Эҙләү",
"prefs-namespaces": "Исем арауыҡтары",
@@ -1203,7 +1203,7 @@
"linterror-tidy-whitespace-bug": "Буш урын символдарын тикшереү хатаһы",
"linterror-unclosed-quotes-in-heading": "\"Тырнаҡ\" ябылмаған",
"yourgender": "Һеҙ үҙегеҙҙе нисек һүрәтләргә теләр инегеҙ?",
- "gender-unknown": "Программа һеҙҙе телгә алғанда мөмкин тиклем енси-нейтраль һүҙҙәр ҡулланасаҡ",
+ "gender-unknown": "Программа һеҙҙе телгә алғанда мөмкин булғанса нейтраль зат һүҙҙәре ҡулланасаҡ",
"gender-notknown": "Вики-биттәрҙе мөхәррирләй",
"gender-male": "Ир-егет вики-биттәрҙе мөхәррирләй",
"gender-female": "Ҡатын-ҡыҙ вики-биттәрҙе мөхәррирләй",
@@ -1223,7 +1223,7 @@
"prefs-signature-highlight-error": "Хата урынын күрһәтергә",
"prefs-signature-error-details": "Күберәк белергә",
"prefs-dateformat": "Дата форматы",
- "prefs-timeoffset": "Ваҡыт бүлкәтенең күсеүе",
+ "prefs-timeoffset": "Ваҡыт бүлкәте шылыуы",
"prefs-advancedediting": "Дөйөм көйләүҙәр",
"prefs-developertools": "Программист ҡоралдары",
"prefs-editor": "Мөхәррир",
diff --git a/languages/i18n/ca.json b/languages/i18n/ca.json
index feac6bc2c4f0..042a41ff0b16 100644
--- a/languages/i18n/ca.json
+++ b/languages/i18n/ca.json
@@ -36,6 +36,7 @@
"Kaganer",
"Kippelboy",
"Leptictidium",
+ "Lluis Cat",
"Lluis tgn",
"Loupeter",
"Macofe",
@@ -959,6 +960,8 @@
"revdelete-radio-same": "(no ho canviïs)",
"revdelete-radio-set": "Oculta",
"revdelete-radio-unset": "Visible",
+ "revdelete-radio-set-suppress": "Elimina",
+ "revdelete-radio-unset-suppress": "Restaura",
"revdelete-suppress": "Suprimeix també les dades dels administradors",
"revdelete-unsuppress": "Suprimir les restriccions de les revisions restaurades",
"revdelete-log": "Motiu:",
@@ -1909,6 +1912,11 @@
"uploadstash-errclear": "S'estan netejant els fitxers que han fallat.",
"uploadstash-refresh": "Actualitza la llista de fitxers",
"uploadstash-exception": "No s'ha pogut emmagatzemar la càrrega en reserva ($1): «$2»",
+ "uploadstash-header-date": "Data",
+ "uploadstash-header-filekey": "Clau",
+ "uploadstash-header-thumb": "Miniatura",
+ "uploadstash-header-dimensions": "Dimensions",
+ "uploadstash-pager-submit": "Mostra la llista de fitxers",
"uploadstash-bad-path": "El camí no existeix.",
"uploadstash-bad-path-invalid": "El camí no és vàlid.",
"uploadstash-bad-path-unknown-type": "El tipus «$1» és desconegut.",
@@ -1953,10 +1961,11 @@
"licenses-edit": "Modifica les opcions de llicència",
"license-nopreview": "(Previsualització no disponible)",
"upload_source_url": " (el fitxer que heu seleccionat des d'un URL vàlid i accessible públicament)",
- "upload_source_file": " (un fitxer triat del vostre ordinador)",
+ "upload_source_file": " (un fitxer triat del vostre dispositiu)",
"listfiles-delete": "elimina",
"listfiles-summary": "Aquesta pàgina especial mostra tots els fitxers carregats.",
"listfiles-userdoesnotexist": "El compte d’usuari «$1» no s’ha registrat.",
+ "listfiles-pager-submit": "Mostra la llista de fitxers",
"imgfile": "fitxer",
"listfiles": "Llista de fitxers",
"listfiles_subpage": "Càrregues per $1",
diff --git a/languages/i18n/ce.json b/languages/i18n/ce.json
index fdc9335b43a2..8b32ec7313e0 100644
--- a/languages/i18n/ce.json
+++ b/languages/i18n/ce.json
@@ -1908,7 +1908,7 @@
"upload-curl-error28-text": "Сайт тӀех дукха Ӏа жоп ца луш. Дехар до, талла сайт болх бан лур болуш йуй, жимма Ӏийна йуха хьажа. Тарло, операци кхечу хенахь йан йеза, сайт чу кӀезга бевлча.",
"license": "Лицензи:",
"license-header": "Лицензи",
- "nolicense": "ХӀума хаьржина йац",
+ "nolicense": "Хӏумма а хаьржина йац",
"licenses-edit": "Лицензин параметраш хийца",
"license-nopreview": "(Хьалха хьажа цало)",
"upload_source_url": "(ахьа хаьржина нийса, массо тӀекхочу интернет-адрес)",
@@ -2213,7 +2213,7 @@
"log-edit-tags": "Тептаран дӀайаздаран тӀера хаьржина тегашна хийцам бе",
"checkbox-select": "Харжар: $1",
"checkbox-all": "Массо",
- "checkbox-none": "ХӀума",
+ "checkbox-none": "ХӀумма",
"checkbox-invert": "Инверт йан",
"allpages": "Массо агӀонаш",
"nextpage": "РогӀера агӀо ($1)",
diff --git a/languages/i18n/chn.json b/languages/i18n/chn.json
index b9692b387041..f46d8c22745a 100644
--- a/languages/i18n/chn.json
+++ b/languages/i18n/chn.json
@@ -8,7 +8,7 @@
"sunday": "Santi",
"monday": "Ixt-sən",
"tuesday": "Makwst-sən",
- "wednesday": "Łun-sən",
+ "wednesday": "Ɬun-sən",
"thursday": "Lakit-sən",
"friday": "K'winəm-sən",
"saturday": "Tax̣əm-sən",
@@ -21,28 +21,28 @@
"sat": "6-san",
"january": "Ixt-mun",
"february": "Makwst-mun",
- "march": "Łun-mun",
+ "march": "Ɬun-mun",
"april": "Lakit-mun",
"may_long": "K'winəm-mun",
"june": "Tax̣əm-mun",
"july": "Sinəmakwst-mun",
"august": "Stuxtkin-mun",
"september": "Kwist-mun",
- "october": "Tałləm-mun",
- "november": "Tałləm-ixt-mun",
- "december": "Tałləm-makwst-mun",
+ "october": "Taɬləm-mun",
+ "november": "Taɬləm-ixt-mun",
+ "december": "Taɬləm-makwst-mun",
"january-gen": "Ixt-mun",
"february-gen": "Makwst-mun",
- "march-gen": "Łun-mun",
+ "march-gen": "Ɬun-mun",
"april-gen": "Lakit-mun",
"may-gen": "K'winəm-mun",
"june-gen": "Tax̣əm-mun",
"july-gen": "Sinəmakwst-mun",
"august-gen": "Stuxtkin-mun",
"september-gen": "Kwist-mun",
- "october-gen": "Tałləm-mun",
- "november-gen": "Tałləm-ixt-mun",
- "december-gen": "Tałləm-makwst-mun",
+ "october-gen": "Taɬləm-mun",
+ "november-gen": "Taɬləm-ixt-mun",
+ "december-gen": "Taɬləm-makwst-mun",
"jan": "01",
"feb": "02",
"mar": "03",
@@ -65,28 +65,28 @@
"category-subcat-count": "{{PLURAL:$2|Ukuk nim-ikta t'uʔan ixt tenas nim-ikta.|Ukuk nim-ikta t'uʔan ukuk{{PLURAL:$1|subcategory|$1 tenas nim-ikta}} (kanawi nim-ikta:$2.)}}",
"category-article-count": "{{PLURAL:$2|Ukuk nim-ikta t'uʔan ixt pipa.|Ukuk nim-ikta t'uʔan ukuk{{PLURAL:$1|subcategory|$1 pipa}} (kanawi pipa:$2.)}}",
"category-file-count": "Ukuk nim-ikta t'uʔan ixt fayl. Kopa ukuk nim-ikta ukuk {{PLURAL:$1|1|$1}} fayl (kanawi fayl: $2)",
- "broken-file-category": "Pipa khanumakwst kəltəs hayu-lakhłwa-lipait",
+ "broken-file-category": "Pipa khanumakwst kəltəs hayu-lakhɬwa-lipait",
"about": "Kanawi pus ukuk",
- "newwindow": "(mamuk x̣alaqł kopa chxi windo)",
+ "newwindow": "(mamuk x̣alaqɬ kopa chxi windo)",
"cancel": "Mash mamuk-ikta",
"mypage": "Pipa",
"mytalk": "Wawa",
"anontalk": "Wawa",
- "navigation": "Łatwa-ilan",
+ "navigation": "Ɬatwa-ilan",
"and": "&#39;pi",
"namespaces": "Kanawi nim-iliʔi",
- "navigation-heading": "\"Łatwa-ilan\" yaka ikta-pipa",
+ "navigation-heading": "\"Ɬatwa-ilan\" yaka ikta-pipa",
"returnto": "Kilapay kopa $1",
"tagline": "Ukuk chako kopa {{SITENAME}}",
"help": "Yeʔlan",
- "search": "Łatwa-nanich",
- "searchbutton": "Łatwa-nanich",
- "go": "Łatwa",
- "searcharticle": "Łatwa!",
+ "search": "Ɬatwa-nanich",
+ "searchbutton": "Ɬatwa-nanich",
+ "go": "Ɬatwa",
+ "searcharticle": "Ɬatwa!",
"history": "Nanich ukuk pipa kakwa ulman yaka",
"history_short": "Nanich ukuk pipa kakwa ulman yaka",
"printableversion": "Maika ts'əm ukuk kopa ts'əm-mashin pus maika tiki",
- "permalink": "Ukuk hayu-lakhlwa-łipayt kwansəm mamuk",
+ "permalink": "Ukuk hayu-lakhlwa-ɬipayt kwansəm mamuk",
"view": "Nanich",
"skin-view-view": "Nanich pipa",
"skin-view-foreign": "Nanich kopa $1",
@@ -105,12 +105,12 @@
"cactions": "Wix̣t",
"otherlanguages": "kopa x̣luima ləlang",
"redirectedfrom": "(Maika chako yakwa kopa $1)",
- "redirectpagesub": "Pipa chako łatwa x̣luima pipa",
- "redirectto": "Ukuk pipa łatwa kopa:",
+ "redirectpagesub": "Pipa chako ɬatwa x̣luima pipa",
+ "redirectto": "Ukuk pipa ɬatwa kopa:",
"lastmodifiedat": "Kopa $1 pi kopa $2 ukuk pipa yaka elip-chxi ts'əm-ikta.",
- "jumpto": "Łatwa kopa:",
- "jumptonavigation": "łatwa-ilan",
- "jumptosearch": "Łatwa-nanich",
+ "jumpto": "Ɬatwa kopa:",
+ "jumptonavigation": "ɬatwa-ilan",
+ "jumptosearch": "Ɬatwa-nanich",
"aboutsite": "Ikta {{SITENAME}}?",
"aboutpage": "Project:Ikta ukuk?",
"copyrightpage": "{{ns:project}}:Kanawi Mamuk-chako-makwst-laysins",
@@ -118,7 +118,7 @@
"currentevents-url": "Project:Wik-kopet-ikta",
"disclaimers": "Tayi wawa-ikta pus maika nanich",
"disclaimerpage": "Project:Tayi wawa-ikta pus maika nanich",
- "edithelp": "yeʔlan pus munk-ts'ǝm",
+ "edithelp": "yeʔlan pus munk-ts'əm",
"mainpage": "Tayi-pipa\n(Note: The Chinook Jargon (chinuk wawa) interface is still in testing and may be of poor quality.)",
"mainpage-description": "Tayi-pipa\n(Note: The Chinook Jargon (chinuk wawa) interface is still in testing and may be of poor quality.)",
"portal": "Tilixam-lapot",
@@ -141,15 +141,15 @@
"nstab-special": "X̣luima pipa",
"nstab-project": "Wik-kəpit-ikta pipa",
"nstab-image": "Fayl",
- "nstab-mediawiki": "Mamuk-łatawa Wawa",
+ "nstab-mediawiki": "Mamuk-ɬatawa Wawa",
"nstab-template": "\"Template\"",
"nstab-help": "yeʔlan pipa",
"nstab-category": "Nim-ikta",
"mainpage-nstab": "Tayi-pipa\nNote: The Chinook Jargon (chinuk wawa) interface is still in testing and may be of poor quality.",
"nosuchspecialpage": "Nesaika wik t'uʔan ukuk x̣luima-pipa",
"nospecialpagetext": "<strong>Nesaika wik t'uʔan ukuk x̣luima-pipa.</strong>\nNanich kanawi x̣luima-pipa kopa [[Special:SpecialPages|{{int:specialpages}}]].",
- "badtitle": "Kǝltǝs pipa-nim",
- "badtitletext": "Kəltəs ukuk pipa yaka nim.\nIxt-ixt maika x̣auqał mamuk ixt ts'əm-ikta epe wix̣t ts'əm-ikta kopa ukuk nim.",
+ "badtitle": "Kəltəs pipa-nim",
+ "badtitletext": "Kəltəs ukuk pipa yaka nim.\nIxt-ixt maika x̣auqaɬ mamuk ixt ts'əm-ikta epe wix̣t ts'əm-ikta kopa ukuk nim.",
"viewsource": "Nanich pipa-lalasin",
"skin-action-viewsource": "Nanich pipa-lalasin",
"viewsource-title": "Nanich lalasin-pipa pus $1",
@@ -163,15 +163,15 @@
"createacct-yourpassword-ph": "Mamuk ipsut-wawa",
"createacct-yourpasswordagain": "Mamuk-delet maika ipsut-wawa",
"createacct-yourpasswordagain-ph": "Wix̣t nanich ipsut-wawa",
- "userlogin-remembermypassword": "Miłait kopa maika laysins kwanisǝm",
- "login": "Łatwa kopa maika laysins",
- "nav-login-createaccount": "Łatwa kopa maika laysins / mamuk laysins",
+ "userlogin-remembermypassword": "Miɬait kopa maika laysins kwanisəm",
+ "login": "Ɬatwa kopa maika laysins",
+ "nav-login-createaccount": "Ɬatwa kopa maika laysins / mamuk laysins",
"notloggedin": "Wik kopa laysins",
"userlogin-noaccount": "Maika wik t'uʔan laysins?",
"userlogin-joinproject": "Mamuk laysins kopa {{SITENAME}}",
"createaccount": "Mamuk laysins",
"userlogin-resetpassword-link": "Maika wik-kəmtəks maika ipsut-wawa?",
- "userlogin-helplink2": "yeʔlan pus łatwa kopa maika laysins",
+ "userlogin-helplink2": "Yeʔlan pus ɬatwa kopa maika laysins",
"createacct-emailoptional": "Munk-ts'əm maika epost-nim pus maika tiki",
"createacct-email-ph": "Munk-ts'əm maika epost-nim",
"createacct-submit": "Mamuk maika laysins",
@@ -179,12 +179,12 @@
"createacct-benefit-heading": "Tilixam kakwa maika mamuk {{SITENAME}}",
"createacct-benefit-body1": "{{PLURAL:$1|ts'əm-ikta}}",
"createacct-benefit-body2": "{{PLURAL:$1|pipa}}",
- "createacct-benefit-body3": "{{PLURAL:$1|ixt|kanawi}} chxi tilixam łaksta mamuk ts'əm yakwa",
+ "createacct-benefit-body3": "{{PLURAL:$1|ixt|kanawi}} chxi tilixam ɬaksta mamuk ts'əm yakwa",
"loginlanguagelabel": "Ləlang: $1",
- "pt-login": "Łatwa kopa maika laysins",
- "pt-login-button": "Łatwa kopa maika laysins",
+ "pt-login": "Ɬatwa kopa maika laysins",
+ "pt-login-button": "Ɬatwa kopa maika laysins",
"pt-createaccount": "Mamuk laysins",
- "pt-userlogout": "Łatwa łax̣ani maika laysins",
+ "pt-userlogout": "Ɬatwa ɬax̣ani maika laysins",
"botpasswords-label-cancel": "Mash mamuk-ikta",
"resetpass-submit-cancel": "Mash mamuk-ikta",
"passwordreset": "Mamuk-kla maika ipsut-wawa",
@@ -201,16 +201,16 @@
"preview": "Nanich ukuk pipa kakwa pus maika wik munk-ts'əm kopa ikta",
"showpreview": "Nanich ukuk pipa kakwa pus maika wik munk-ts'əm kopa ikta",
"showdiff": "Nanich kanawi chako-x̣luima-ikta",
- "anoneditwarning": "Maika wik kopa laysins. Tilixam nanich maika IP-k'winin-ikta pus maika munk-ts'əm kopa pipa. Pus maika <strong>[$1 łatwa kopa maika laysins]</strong> epe <strong>[$2 mamuk laysins]</strong>, kanawi maika ts'əm-ikta tuʔan khanumakwst maika nim, pi maika iskəm x̣luima łush ikta.",
- "blockedtext": "<strong>Maika x̣auqał munk-ts'əm kanawi pipa pus maika mamuk ukuk IP-k'winin-ikta epe tilixam-nim.</strong>\n{{int:blockedtext-made-by|$1}}\n{{int:blockedtext-reason-comment|$2}}.\n\n*{{int:blockedtext-start-time|$8}}\n*{{int:blockedtext-expiration-time|$6}}\n*{{int:blockedtext-intended-blockee|$7}}\n\n{{int:blockedtext-contact-blocker-admin|$1}}\n\nMamuk \"{{int:emailuser}}\" pus kopa maika [[Special:Preferences|preferences]] ixt delet e-post yaka nim, pi maika wik x̣auqał mamuk ukuk laysins.\n{{int:blockedtext-block-ip|$3}} {{int:blockedtext-block-id|$5}}\n{{int:blockedtext-include-details-queries}}",
- "loginreqlink": "łatwa kopa maika laysins",
- "newarticletext": "Maika łatwa kopa hayu-lakhlwa-łipait pi alta kopa kǝltǝs pipa maika. Pus maika tiki mamuk ukuk pipa, mamuk ts'ǝm kopa kikwǝli-lakhaset (nanich [$1 help page|elip-pipa kopa $1] pus maika tiki wix̣t kǝmtǝks.)",
- "anontalkpagetext": "----\n<em>Tilixam hilu laysins yaka wawa-pipa ukuk, pi naika nanich yaka IP-k'winin-ikta. Ixt-ixt hayu tilixam t'uʔan ixt IP-k'winin-ikta.\nPus wik kopa laysins maika, [[Special:CreateAccount\n|mamuk laysins]] epe [[Special:UserLogin|łatwa kopa maika laysins]] pus maika tiki.",
- "noarticletext": "Halo ts'əm-ikta ukuk pipa.\n[[Special:Search/{{PAGENAME}}|Łatwa-nanich ukuk pipa yaka nim]] pus maika tiki,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} łatwa-nanich kapho-log],\nepe [{{fullurl:{{FULLPAGENAME}}|action=edit}} mamuk ukuk pipa.]</span>.",
- "noarticletext-nopermission": "Halo tsəm ukuk pipa. Naika [[Special:Search/{{PAGENAME}}|nanich-łatwa ukuk pipa yaka nim]] kopa x̣luima pipa, epe <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} x̣luima kanawi ulman kapho ts'əm]</span> pus naika tiki. Maika x̣awqał mamuk ukuk pipa.",
+ "anoneditwarning": "Maika wik kopa laysins. Tilixam nanich maika IP-k'winin-ikta pus maika munk-ts'əm kopa pipa. Pus maika <strong>[$1 ɬatwa kopa maika laysins]</strong> epe <strong>[$2 mamuk laysins]</strong>, kanawi maika ts'əm-ikta tuʔan khanumakwst maika nim, pi maika iskəm x̣luima ɬush ikta.",
+ "blockedtext": "<strong>Maika x̣auqaɬ munk-ts'əm kanawi pipa pus maika mamuk ukuk IP-k'winin-ikta epe tilixam-nim.</strong>\n{{int:blockedtext-made-by|$1}}\n{{int:blockedtext-reason-comment|$2}}.\n\n*{{int:blockedtext-start-time|$8}}\n*{{int:blockedtext-expiration-time|$6}}\n*{{int:blockedtext-intended-blockee|$7}}\n\n{{int:blockedtext-contact-blocker-admin|$1}}\n\nMamuk \"{{int:emailuser}}\" pus kopa maika [[Special:Preferences|preferences]] ixt delet e-post yaka nim, pi maika wik x̣auqaɬ mamuk ukuk laysins.\n{{int:blockedtext-block-ip|$3}} {{int:blockedtext-block-id|$5}}\n{{int:blockedtext-include-details-queries}}",
+ "loginreqlink": "ɬatwa kopa maika laysins",
+ "newarticletext": "Maika ɬatwa kopa hayu-lakhlwa-ɬipait pi alta kopa kəltəs pipa maika. Pus maika tiki mamuk ukuk pipa, mamuk ts'əm kopa kikwəli-lakhaset (nanich [$1 help page|elip-pipa kopa $1] pus maika tiki wix̣t kəmtəks.)",
+ "anontalkpagetext": "----\n<em>Tilixam hilu laysins yaka wawa-pipa ukuk, pi naika nanich yaka IP-k'winin-ikta. Ixt-ixt hayu tilixam t'uʔan ixt IP-k'winin-ikta.\nPus wik kopa laysins maika, [[Special:CreateAccount\n|mamuk laysins]] epe [[Special:UserLogin|ɬatwa kopa maika laysins]] pus maika tiki.",
+ "noarticletext": "Halo ts'əm-ikta ukuk pipa.\n[[Special:Search/{{PAGENAME}}|Ɬatwa-nanich ukuk pipa yaka nim]] pus maika tiki,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ɬatwa-nanich kapho-log],\nepe [{{fullurl:{{FULLPAGENAME}}|action=edit}} mamuk ukuk pipa.]</span>.",
+ "noarticletext-nopermission": "Halo tsəm ukuk pipa. Naika [[Special:Search/{{PAGENAME}}|nanich-ɬatwa ukuk pipa yaka nim]] kopa x̣luima pipa, epe <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} x̣luima kanawi ulman kapho ts'əm]</span> pus naika tiki. Maika x̣awqaɬ mamuk ukuk pipa.",
"userpage-userdoesnotexist-view": "Ukuk wiki wik t'uʔan ixt tilixam khanumakst ukuk nim: \"$1\".",
"previewnote": "<strong>Təmtəm alta: ukuk pipa wik delet!</strong>",
- "continue-editing": "Łatwa kopa ts'əm-iliʔi.",
+ "continue-editing": "Ɬatwa kopa ts'əm-iliʔi.",
"editing": "Maika alta munk tsəm kopa \"$1\".",
"creating": "Maika mamuk \"$1\".",
"editingsection": "Munk-ts'əm kopa $1(sitkəm)",
@@ -220,8 +220,8 @@
"template-semiprotected": "(tenas-skukum pipa)",
"hiddencategories": "Kopa {{PLURAL:$1|ixt nim-ikta maika wik nanich|$1 nim-ikta maika wik nanich}}: ukuk pipa",
"permissionserrors": "Laysins-tsipi",
- "permissionserrorstext-withaction": "Maika x̣auqał $2, qiwa {{PLURAL:$1|ukuk|kanawi ukuk}}:",
- "recreate-moveddeleted-warn": "<strong>Ankəti ixt tilixam mash ukuk pipa.</strong>\n\nTəmtəm kakwa pus łush maika wixt mamuk ukuk pipa.\nNanich kanawi taym tilixam łatwa epe mash ukuk pipa:",
+ "permissionserrorstext-withaction": "Maika x̣auqaɬ $2, qiwa {{PLURAL:$1|ukuk|kanawi ukuk}}:",
+ "recreate-moveddeleted-warn": "<strong>Ankəti ixt tilixam mash ukuk pipa.</strong>\n\nTəmtəm kakwa pus ɬush maika wixt mamuk ukuk pipa.\nNanich kanawi taym tilixam ɬatwa epe mash ukuk pipa:",
"moveddeleted-notice": "Nesaika wik t'uʔan ixt pipa khanumakwst ukuk nim.\n(nanich kikwili)",
"content-model-wikitext": "wikitext",
"undo-failure": "Maika wik kilapai kopa wixt-ulman pipa, qiwa wix̣t-chxi ts'əm katsəq.",
@@ -251,10 +251,10 @@
"compareselectedversions": "Nanich kanawi ukuk pik-ts'əm-ikta:",
"editundo": "kilapay",
"diff-empty": "(delet wik x̣luima)",
- "diff-multi-sameuser": "({{GENDER:$3|user}} munk-ts'ǝm wik-x̣luima ({{PLURAL:$1|tsum-ikta kopa katsǝq}})(maika wik nanich ukuk))",
- "diff-multi-otherusers": "maika wik nanich {{PLURAL:$2|ixt tilixam yaka|$2 tilixam łaska}} {{PLURAL:$1|katsaq-ts'əm-ikta|$1 katsaq-ts'əm-ikta}}",
- "searchresults": "Ikta maika łap",
- "searchresults-title": "Ikta maika łap pus \"$1\":",
+ "diff-multi-sameuser": "({{GENDER:$3|user}} munk-ts'əm wik-x̣luima ({{PLURAL:$1|tsum-ikta kopa katsəq}})(maika wik nanich ukuk))",
+ "diff-multi-otherusers": "maika wik nanich {{PLURAL:$2|ixt tilixam yaka|$2 tilixam ɬaska}} {{PLURAL:$1|katsaq-ts'əm-ikta|$1 katsaq-ts'əm-ikta}}",
+ "searchresults": "Ikta maika ɬap",
+ "searchresults-title": "Ikta maika ɬap pus \"$1\":",
"prevn": "wix̣t-elip {{PLURAL:$1}}",
"nextn": "wix̣t-kimta {{PLURAL:$1|$1}}",
"prev-page": "pipa elip",
@@ -264,7 +264,7 @@
"shown-title": "Nanich $1 {{PLURAL:$1|ikta}} kopa kanawi pipa",
"viewprevnext": "Nanich ($1 {{int:pipe-separator}} $2) ($3)",
"searchmenu-exists": "{{SITENAME}} t'uʔan ukuk pipa khanumakwst uk nim \"[[:$1]]\". {{PLURAL:$2|0=|Nanich wix̣t x̣luima ikta kikwəli.}}",
- "searchmenu-new": "Mamuk pipa \"[[:$1]]\" kopa ukuk wiki! {{PLURAL:$2|0=|Wixt nanich pipa maika łap kopa maika łatwa-nanich-ikta.|Wixt nanich kanawi pipa maika łap kopa maika łatwa-nanich-ikta.}}",
+ "searchmenu-new": "Mamuk pipa \"[[:$1]]\" kopa ukuk wiki! {{PLURAL:$2|0=|Wixt nanich pipa maika ɬap kopa maika ɬatwa-nanich-ikta.|Wixt nanich kanawi pipa maika ɬap kopa maika ɬatwa-nanich-ikta.}}",
"searchprofile-articles": "Kanawi pipa khanumakwst delet ikta",
"searchprofile-images": "Multimedia",
"searchprofile-everything": "Kanawi ikta",
@@ -274,13 +274,13 @@
"search-redirect": "($1 lulu maika yakwa)",
"search-section": "(tenas-sitkəm $1)",
"search-file-match": "(wik-x̣luima kakwa fayl yaka ikta)",
- "search-suggest": "Maika tiki łap: $1?",
+ "search-suggest": "Maika tiki ɬap: $1?",
"searchall": "kanawi",
"search-showingresults": "{{PLURAL:$4|K'ilapai-wawa <strong>$1</strong> kopa <strong>$3</strong>|K'ilapai-wawa <strong>$1 – $2</strong> kopa <strong>$3</strong>}}",
- "search-nonefound": "Halo ikta kakwa maika łatwa-nanich-ikta ukuk wiki.",
+ "search-nonefound": "Halo ikta kakwa maika ɬatwa-nanich-ikta ukuk wiki.",
"mypreferences": "Maika tiki wix̣t ukuk ikta",
- "searchresultshead": "Łatwa-nanich",
- "prefs-searchoptions": "Łatwa-nanich",
+ "searchresultshead": "Ɬatwa-nanich",
+ "prefs-searchoptions": "Ɬatwa-nanich",
"userrights-reason": "Qhata:",
"group-bot": "Kanawi lamashin yaka laysins",
"group-sysop": "Tayi tilixam",
@@ -292,7 +292,7 @@
"enhancedrc-history": "Nanich ukuk pipa kakwa ulman yaka",
"recentchanges": "Chxi chako-x̣luima-ikta",
"recentchanges-summary": "Kwanisəm nanich elip chxi ts'əm-ikta kopa ukuk pipa.",
- "recentchanges-noresult": "Nsaika wik t'uʔan chxi tsəm-ikta kopa ukuk taym pus maika łatwa-nanich-ikta.",
+ "recentchanges-noresult": "Nsaika wik t'uʔan chxi tsəm-ikta kopa ukuk taym pus maika ɬatwa-nanich-ikta.",
"recentchanges-label-plusminus": "Ukuk pipa yaka hayas-ikta chako-x̣luima kopa ukuk bait yaka k'winin-ikta",
"recentchanges-legend-heading": "<strong>Wawa-snohus:</strong>",
"recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (nanich wix̣t [[Special:NewPages|kanawi chxi pipa]])",
@@ -305,13 +305,13 @@
"rcshowhideminor": "$1 tenas tsəm",
"rcshowhideminor-show": "Nanich",
"rcshowhideminor-hide": "Mamuk-chako-ipsut",
- "rcshowhidebots": "$1 mashin łaska laysins",
+ "rcshowhidebots": "$1 mashin ɬaska laysins",
"rcshowhidebots-show": "Nanich",
"rcshowhidebots-hide": "Mamuk-chako-ipsut",
"rcshowhideliu": "$1 Tilixam khanumakwst delet laysins",
"rcshowhideliu-show": "Nanich",
"rcshowhideliu-hide": "Mamuk-chako-ipsut",
- "rcshowhideanons": "$1 nesaika wik təmtəm ukuk tilixam łaska nim",
+ "rcshowhideanons": "$1 nesaika wik təmtəm ukuk tilixam ɬaska nim",
"rcshowhideanons-show": "Nanich",
"rcshowhideanons-hide": "Mamuk-chako-ipsut",
"rcshowhidepatr": "$1 tilixam kwanisum nanich ukuk munk-ts'əm-ikta",
@@ -331,10 +331,10 @@
"recentchangeslinked": "Kanawi Wik-hayu-x̣luima chako-x̣luima",
"recentchangeslinked-toolbox": "Kanawi Wik-hayu-x̣luima chako-x̣luima",
"recentchangeslinked-title": "$1 yaka kapho chako-x̣luima-ikta.",
- "recentchangeslinked-summary": "Munk-ts'əm pipa yaka nim pus maika nanich chxi ts'əm-ikta kopa epe wik-saia ukuk pipa. (Pus naika tikix nanich pipa kopa ixt nim-ikta, mamuk-ts'əm {{ns:category}}:Name of category). Kopa p'iłił tsəm chxi ts'əm-ikta kopa [[Special:Watchlist|maika Watchlist]]",
+ "recentchangeslinked-summary": "Munk-ts'əm pipa yaka nim pus maika nanich chxi ts'əm-ikta kopa epe wik-saia ukuk pipa. (Pus naika tikix nanich pipa kopa ixt nim-ikta, mamuk-ts'əm {{ns:category}}:Name of category). Kopa p'iɬiɬ tsəm chxi ts'əm-ikta kopa [[Special:Watchlist|maika Watchlist]]",
"recentchangeslinked-page": "Pipa yaka nim:",
- "recentchangeslinked-to": "Nanich chxi ts'əm-ikta kopa x̣luima pipa pus łaska hayu-lakhlwa-łipait łatwa kopa ukuk pipa.",
- "upload": "Pałach ikta",
+ "recentchangeslinked-to": "Nanich chxi ts'əm-ikta kopa x̣luima pipa pus ɬaska hayu-lakhlwa-ɬipait ɬatwa kopa ukuk pipa.",
+ "upload": "Paɬach ikta",
"filedesc": "Kakwa tenas ts'əm",
"upload-dialog-button-cancel": "Mash mamuk-ikta",
"license": "Kanawi ukuk fayl yaka laysins",
@@ -342,7 +342,7 @@
"imgfile": "fayl",
"file-anchor-link": "Fayl",
"filehist": "Nanich ukuk ikta kakwa ulman yaka",
- "filehist-help": "Łatwa ukuk sun/taym pus maika tiki nanich fayl kopa ukuk taym.",
+ "filehist-help": "Ɬatwa ukuk sun/taym pus maika tiki nanich fayl kopa ukuk taym.",
"filehist-deleteone": "mash ukuk",
"filehist-revert": "Mamuk ukuk pipa kilapay kopa qhata ankəti yaka",
"filehist-current": "Kakwa alta",
@@ -356,15 +356,15 @@
"imagelinks": "Qha nesaika mamuk ukuk ikta",
"sharedupload-desc-here": "Chako kopa $1 ukuk fayl, pi x̣luima wik-kopet-ikta mamuk ukuk. Nanich ukuk fayl yaka [$2 wawa-ikta-pipa] kikwili.",
"filepage-nofile": "Nesaika wik t'uʔan ukuk fayl khanumakwst ukuk nim",
- "upload-disallowed-here": "Naika x̣awqał munk-tsəm sax̣ali ukuk fayl.",
+ "upload-disallowed-here": "Naika x̣awqaɬ munk-tsəm sax̣ali ukuk fayl.",
"filerevert-comment": "Qhata:",
"filedelete-comment": "Qhata:",
"filedelete-submit": "Mash ukuk",
"randompage": "Nsaika pik ixt pipa",
"randomincategory-category": "Nim-ikta:",
- "randomincategory-submit": "Łatwa",
+ "randomincategory-submit": "Ɬatwa",
"statistics": "K'winin-ikta",
- "pageswithprop-submit": "Łatwa",
+ "pageswithprop-submit": "Ɬatwa",
"withoutinterwiki-submit": "Nanich",
"nmembers": "$1 {{PLURAL:$1|tilixam}} yakwa",
"prefixindex": "Kanawi pipa khanumakwst elip-ts'əm-ikta",
@@ -374,29 +374,29 @@
"listusers": "Kanawi tilixam yakwa",
"newpages": "Kanawi chxi pipa",
"newpages-username": "Nim:",
- "move": "Mamuk-łatwa",
+ "move": "Mamuk-ɬatwa",
"pager-newer-n": "{{PLURAL:$1|wix̣t-chxi 1|newer $1}}",
"pager-older-n": "{{PLURAL:$1|wix̣t-ulman 1|wix̣t-ulman $1}}",
- "booksources": "Buk pus maika iskəm ikta kopa łaska",
- "booksources-search-legend": "Łatwa-nanich lalasin-pipa kopa buk",
- "booksources-search": "Łatwa-nanich",
- "specialloguserlabel": "Tilixam łaksta mamuk ukuk:",
+ "booksources": "Buk pus maika iskəm ikta kopa ɬaska",
+ "booksources-search-legend": "Ɬatwa-nanich lalasin-pipa kopa buk",
+ "booksources-search": "Ɬatwa-nanich",
+ "specialloguserlabel": "Tilixam ɬaksta mamuk ukuk:",
"speciallogtitlelabel": "Nanich (pipa-nim epe {{ns:user}}:ukuk tilixam yaka nim):",
"log": "Kanawi \"stik\"",
"logeventslist-submit": "Nanich",
- "all-logs-page": "Kanawi stik pus maika wik-x̣auqał nanich łaska",
+ "all-logs-page": "Kanawi stik pus maika wik-x̣auqaɬ nanich ɬaska",
"alllogstext": "Yakwa Nanich kanawi {{SITENAME}} yaka \"stik\". \nNanich wix̣t-wik-hayu ikta pus maika pik ixt stik-nim-ikta, ixt tilixam (təmtəm kakwa tsəm-ikta-hayas) epe ixt pipa (wix̣t təmtəm kakwa tsəm-ikta-hayas).",
"logempty": "Halo wik-x̣luima ikta kopa ukuk stik",
"allpages": "Kanawi pipa",
"allarticles": "Kanawi pipa",
- "allpagessubmit": "Łatwa!",
- "allpages-hide-redirects": "Wik nanich pipa pus łaska łatwa kopa x̣luima pipa.",
+ "allpagessubmit": "Ɬatwa!",
+ "allpages-hide-redirects": "Wik nanich pipa pus ɬaska ɬatwa kopa x̣luima pipa.",
"categories": "Nim-ikta",
"categories-submit": "Nanich",
- "linksearch-ok": "Łatwa-nanich",
+ "linksearch-ok": "Ɬatwa-nanich",
"listusers-submit": "Nanich",
"listgrouprights-members": "(kanawi tilixam kopa yakwa)",
- "emailuser": "Pałach post kopa ukuk tilixam",
+ "emailuser": "Paɬach post kopa ukuk tilixam",
"emailusername": "Nim:",
"emailusernamesubmit": "Mamuk ukuk",
"watchlist": "Kanawi pipa mayka nanich",
@@ -405,12 +405,12 @@
"watch": "Kanawi nanich ukuk",
"unwatch": "Wik kwanisəm nanich",
"watchlist-details": "{{PLURAL:$1|$1 pipa}} on \"kanawi pipa maika kwanisəm nanich\" (pi wix̣t wawa-pipa).",
- "wlheader-showupdated": "Nanich pipa khanumakwst chxi ts'əm-ikta ałqi maika łatwa kopa łaska kakwa <strong>p'iłił ts'əm</strong>.",
+ "wlheader-showupdated": "Nanich pipa khanumakwst chxi ts'əm-ikta aɬqi maika ɬatwa kopa ɬaska kakwa <strong>p'iɬiɬ ts'əm</strong>.",
"wlnote": "Nanich {{PLURAL:$1|elip-chxi ts'əm-ikta<strong>$1</strong> kanawi elip-chxi ts'əm-ikta}} kopa chxi {{PLURAL:$2|awir|<strong>$2</strong> awir}} (kopa $3, $4, kikwəli)",
"watchlist-hide": "Mamuk-chako-ipsut",
"watchlist-submit": "Nanich",
"watchlist-options": "Pik-ikta pus \"kanawi pipa maika nanich\"",
- "enotif_reset": "Wawa kopa kanawi pipa kakwa maika nanich kanawi łaska",
+ "enotif_reset": "Wawa kopa kanawi pipa kakwa maika nanich kanawi ɬaska",
"delete-legend": "Mash ukuk",
"deletecomment": "Qhata:",
"rollback-confirmation-no": "Mash mamuk-ikta",
@@ -418,12 +418,12 @@
"rollbacklinkcount": "mash $1 {{PLURAL:$1|chxi ts'əm-ikta}}",
"changecontentmodel-reason-label": "Qhata:",
"protectcomment": "Qhata:",
- "protect-default": "Pałach kanawi tilixam laysins pus ukuk.",
+ "protect-default": "Paɬach kanawi tilixam laysins pus ukuk.",
"restriction-edit": "Mamuk-ts'əm kopa ukuk",
- "restriction-move": "Mamuk-łatawa ukuk",
+ "restriction-move": "Mamuk-ɬatawa ukuk",
"undeleteviewlink": "nanich",
"undeletecomment": "Qhata:",
- "undelete-search-submit": "Łatwa-nanich",
+ "undelete-search-submit": "Ɬatwa-nanich",
"namespace": "Nim-iliʔi:",
"blanknamespace": "(Tayi)",
"contributions": "Kanawi ikta ukuk tilixam mamuk",
@@ -431,48 +431,48 @@
"mycontris": "Kanawi ikta ukuk tilixam mamuk",
"anoncontribs": "Kanawi ikta ukuk tilixam mamuk",
"contribsub2": "Pus {{GENDER:$3|$1}} ($2)",
- "nocontribs": "Wik nsaika tłap pipa kakwa ikta maika tiki.",
+ "nocontribs": "Wik nsaika tɬap pipa kakwa ikta maika tiki.",
"uctop": "pipa kakwa alta",
"month": "Kopa ixt kul (pi elip):",
"year": "Kopa ixt kul (pi elip):",
- "sp-contributions-blocklog": "mamuk-x̣auqał-munk-ts'əm-stik",
- "sp-contributions-uploads": "fayl-pałach",
+ "sp-contributions-blocklog": "mamuk-x̣auqaɬ-munk-ts'əm-stik",
+ "sp-contributions-uploads": "fayl-paɬach",
"sp-contributions-logs": "\"stik\"",
"sp-contributions-talk": "wawa",
- "sp-contributions-search": "Łatwa-nanich ukuk tilixam yaka ts'əm-ikta",
+ "sp-contributions-search": "Ɬatwa-nanich ukuk tilixam yaka ts'əm-ikta",
"sp-contributions-username": "Ip-k'winin-ikta epe laysins:",
"sp-contributions-toponly": "Nanich chxi ts'əm-ikta pi wik x̣luima ikta",
"sp-contributions-newonly": "Nanich qənchi pipa-mamuk-ikta pi wik x̣luima",
- "sp-contributions-submit": "Łatwa-nanich",
+ "sp-contributions-submit": "Ɬatwa-nanich",
"whatlinkshere": "Ukuk pipa t'uʔan hayu-lakhlwa-lipayt kopa yakwa",
- "whatlinkshere-title": "Ukuk pipa łatwa kopa \"$1\"",
+ "whatlinkshere-title": "Ukuk pipa ɬatwa kopa \"$1\"",
"whatlinkshere-page": "Pipa:",
- "linkshere": "Kanawi ukuk pipa t'uʔan hayak-likhlwa-łipait pus <strong>$2</strong>:",
- "nolinkshere": "Wik pipa t'uʔan hayu-lakhlwa-łipait pus <strong>$2</strong>.",
- "isredirect": "(pipa łatwa kopa x̣luima pipa)",
- "istemplate": "ts'əm-ikta pus łaska chako kopa x̣luima pipa",
- "isimage": "fayl yaka hayu-lakhlwa-łipayt",
+ "linkshere": "Kanawi ukuk pipa t'uʔan hayak-likhlwa-ɬipait pus <strong>$2</strong>:",
+ "nolinkshere": "Wik pipa t'uʔan hayu-lakhlwa-ɬipait pus <strong>$2</strong>.",
+ "isredirect": "pipa ɬatwa kopa x̣luima pipa",
+ "istemplate": "ts'əm-ikta pus ɬaska chako kopa x̣luima pipa",
+ "isimage": "fayl yaka hayu-lakhlwa-ɬipayt",
"whatlinkshere-prev": "{{PLURAL:$1|wix̣t-elip $1}}",
"whatlinkshere-next": "{{PLURAL:$1|wix̣t-kimta $1}}",
- "whatlinkshere-links": "← hayu-lakhlwa łipayt",
- "whatlinkshere-hideredirs": "Wik nanich pipa pus łaska łatwa kopa x̣luima pipa.",
- "whatlinkshere-hidetrans": "Mamuk-ipsut ts'əm-ikta pus łaska chako kopa x̣luima pipa",
- "whatlinkshere-hidelinks": "Wik nanich hayu-lakhlwa-łipait",
- "whatlinkshere-hideimages": "Wik nanich fayl yaka hayu-lakhlwa-łipait",
- "whatlinkshere-submit": "Łatwa",
+ "whatlinkshere-links": "← hayu-lakhlwa ɬipayt",
+ "whatlinkshere-hideredirs": "Wik nanich pipa pus ɬaska ɬatwa kopa x̣luima pipa.",
+ "whatlinkshere-hidetrans": "Mamuk-ipsut ts'əm-ikta pus ɬaska chako kopa x̣luima pipa",
+ "whatlinkshere-hidelinks": "Wik nanich hayu-lakhlwa-ɬipait",
+ "whatlinkshere-hideimages": "Wik nanich fayl yaka hayu-lakhlwa-ɬipait",
+ "whatlinkshere-submit": "Ɬatwa",
"ipbreason": "Qhata:",
"block-reason": "Qhata:",
- "autoblocklist-submit": "Łatwa-nanich",
+ "autoblocklist-submit": "Ɬatwa-nanich",
"blocklist-reason": "Qhata",
- "ipblocklist-submit": "Łatwa-nanich",
+ "ipblocklist-submit": "Ɬatwa-nanich",
"infiniteblock": "halo kimta-ikta",
- "blocklink": "mamuk-x̣auqał-munk-ts'əm",
- "contribslink": "Tilixam łaksta mamuk-ts'əm yakwa",
- "block-log-flags-nocreate": "Maika x̣auqał mamuk laysins yakwa.",
+ "blocklink": "mamuk-x̣auqaɬ-munk-ts'əm",
+ "contribslink": "Tilixam ɬaksta mamuk-ts'əm yakwa",
+ "block-log-flags-nocreate": "Maika x̣auqaɬ mamuk laysins yakwa.",
"newtitle": "Chxi pipa-nim",
"movereason": "Qhata:",
- "export": "Munk-miłait pipa kopa x̣luima inanet-iliʔi",
- "tooltip-search": "Nanich-łatawa kopa {{SITENAME}}",
+ "export": "Munk-miɬait pipa kopa x̣luima inanet-iliʔi",
+ "tooltip-search": "Nanich-ɬatawa kopa {{SITENAME}}",
"pageinfo-hidden-categories": "{{PLURAL:$1|Maika wik nanich ukuk nim-ikta|Maika wik nanich kanawi ukuk nim-ikta}} ($1)",
"pageinfo-toolboxlink": "Pipa yaka kəmtəks",
"patrol-log-page": "Stik pus qənchi tilixam kwanisum nanich ukuk pipa",
@@ -480,11 +480,11 @@
"previousdiff": "Wix̣t ulman pipa",
"nextdiff": "Wix̣t chxi pipa",
"widthheightpage": "$1 × $2, $3 {{PLURAL:$3|pipa}}",
- "file-nohires": "Maika x̣auqał łap ukuk pipa łap wix̣t-hayas.",
+ "file-nohires": "Maika x̣auqaɬ ɬap ukuk pipa ɬap wix̣t-hayas.",
"show-big-image": "Elip fayl",
"show-big-image-preview": "Ukuk piktyur yaka hayas-kansi: $1.",
"show-big-image-other": "X̣luima {{PLURAL:$2|piktyur-hayas-kansi}}: $1.",
- "ilsubmit": "Łatwa-nanich",
+ "ilsubmit": "Ɬatwa-nanich",
"metadata": "Metadata",
"metadata-help": "Ukuk fayl t'uʔan x̣luima kəmtəks, pi nsaika təmtəm: tilixam mamuk-\"skan\" ukuk kəmtəks qənchi yaka mamuk kəmtəks.\nPus elip-ulman fayl chako kakwa x̣luima fayl, ixt-ixt kəltəs tenas kəmtəks.",
"monthsall": "kanawi",
@@ -494,20 +494,20 @@
"confirm-rollback-button": "Aha",
"imgmultipageprev": "← pipa elip",
"imgmultipagenext": "pipa kimta →",
- "imgmultigo": "Łatwa!",
- "imgmultigoto": "Łatwa kopa pipa $1",
- "img-lang-go": "Łatwa",
+ "imgmultigo": "Ɬatwa!",
+ "imgmultigoto": "Ɬatwa kopa pipa $1",
+ "img-lang-go": "Ɬatwa",
"table_pager_next": "Pipa kimta",
"table_pager_prev": "Pipa elip",
- "table_pager_limit_submit": "Łatwa",
+ "table_pager_limit_submit": "Ɬatwa",
"watchlisttools-clear": "Mash kanawi ikta maika kwanisəm-nanich",
"watchlisttools-view": "Nanich tayi chxi-ts'əm",
"watchlisttools-edit": "Nanich pi munk-ts'əm kopa \"kanawi pipa maika nanich\"",
"watchlisttools-raw": "Munk-ts'əm kopa \"kanawi pipa maika nanich\"",
"signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|wawa]])",
"signature-temp": "[[{{#special:Contributions}}/$1|$2]] ([[{{ns:user_talk}}:$1|wawa]])",
- "redirect-submit": "Łatwa",
- "fileduplicatesearch-submit": "Łatwa-nanich",
+ "redirect-submit": "Ɬatwa",
+ "fileduplicatesearch-submit": "Ɬatwa-nanich",
"specialpages": "Kanawi x̣luima pipa",
"tags-active-yes": "Nawitka",
"tags-active-no": "Wik",
@@ -522,19 +522,19 @@
"revdelete-content-hid": "maika wik nanich kanawi ikta",
"feedback-cancel": "Mash mamuk-ikta",
"feedback-submit": "Mamuk ukuk",
- "searchsuggest-search": "Nanich-łatawa kopa {{SITENAME}}",
- "searchsuggest-containing": "Tlai łap pipa kanumakwst",
+ "searchsuggest-search": "Nanich-ɬatawa kopa {{SITENAME}}",
+ "searchsuggest-containing": "Tlai ɬap pipa kanumakwst",
"duration-days": "$1 {{PLURAL:$1sən}}",
"expand_templates_ok": "Aha",
"pagelang-name": "Pipa",
"pagelang-reason": "Qhata",
"pagelang-submit": "Mamuk ukuk",
"paramvalidator-help-type-user-subtype-name": "nim",
- "blockedtext-made-by": "$1 mamuk ukuk tilixam chako x̣awqał munk-ts'əm.",
+ "blockedtext-made-by": "$1 mamuk ukuk tilixam chako x̣awqaɬ munk-ts'əm.",
"blockedtext-reason-comment": "Qhata? <em>$1</em>",
- "blockedtext-start-time": "x̣awqał-munk-ts'əm yaka chako-taym: $1",
- "blockedtext-expiration-time": "x̣awqał-munk-ts'əm yaka elip-kimta-taym: $1",
- "blockedtext-intended-blockee": "Tilixam ukuk tilixam yaka tiki mamuk-x̣auqał-munk-ts'əm: $1",
+ "blockedtext-start-time": "x̣awqaɬ-munk-ts'əm yaka chako-taym: $1",
+ "blockedtext-expiration-time": "x̣awqaɬ-munk-ts'əm yaka elip-kimta-taym: $1",
+ "blockedtext-intended-blockee": "Tilixam ukuk tilixam yaka tiki mamuk-x̣auqaɬ-munk-ts'əm: $1",
"blockedtext-contact-blocker-admin": "Wawa kopa $1 epe x̣luima [[{{MediaWiki:Grouppage-sysop}}|tayi-tilixam]] pus maika tiki wawa pus ukuk.",
"blockedtext-block-ip": "Maika IP-k'winin-ikta $1.",
"blockedtext-include-details-queries": "Mamuk kanawi sax̣ali-ikta kopa kanawi pus-ikta pus maika mamuk ukuk pus-ikta.",
diff --git a/languages/i18n/cv.json b/languages/i18n/cv.json
index 62200c67d8e3..f5fa0da3879e 100644
--- a/languages/i18n/cv.json
+++ b/languages/i18n/cv.json
@@ -1093,6 +1093,7 @@
"prefs-searchmisc": "Пӗтӗмӗшле",
"searchprefs": "Ҫак ӗнерлевсемпе шырамалла",
"searchprefs-noresults": "Нимӗн те тупӑнмарӗ",
+ "searchprefs-results": "$1 {{PLURAL:$1|результат}}",
"saveprefs": "Ҫырса хур",
"restoreprefs": "Пуҫламӑш ӗнерлевсене каялла тавӑрмалла",
"prefs-editing": "Тӳрлетни",
diff --git a/languages/i18n/de.json b/languages/i18n/de.json
index 286d6dc8d82d..9dec443a3450 100644
--- a/languages/i18n/de.json
+++ b/languages/i18n/de.json
@@ -1205,6 +1205,7 @@
"prefs-searchmisc": "Allgemein",
"searchprefs": "Einstellungen suchen",
"searchprefs-noresults": "Keine Ergebnisse",
+ "searchprefs-results": "$1 {{PLURAL:$1|Ergebnis|Ergebnisse}}",
"saveprefs": "Einstellungen speichern",
"restoreprefs": "Standardeinstellungen wiederherstellen",
"prefs-editing": "Bearbeiten",
diff --git a/languages/i18n/en.json b/languages/i18n/en.json
index 6c1f8e7476fa..398ccc7fb939 100644
--- a/languages/i18n/en.json
+++ b/languages/i18n/en.json
@@ -1155,6 +1155,7 @@
"prefs-searchmisc": "General",
"searchprefs": "Search preferences",
"searchprefs-noresults": "No results",
+ "searchprefs-results": "$1 {{PLURAL:$1|result|results}}",
"saveprefs": "Save",
"restoreprefs": "Restore all default settings",
"prefs-editing": "Editing",
diff --git a/languages/i18n/et.json b/languages/i18n/et.json
index 5c2d3468080d..20ccc075f6fe 100644
--- a/languages/i18n/et.json
+++ b/languages/i18n/et.json
@@ -84,6 +84,7 @@
"tog-requireemail": "Saada parooli lähtestamise e-kiri ainult siis, kui ära on toodud nii e-posti aadress kui ka kasutajanimi.",
"tog-forcesafemode": "Kasuta alati [[mw:Manual:Safemode|turvalist režiimi]]",
"tog-editrecovery": "Kasuta [[Special:EditRecovery|muudatuste taastamise]] funktsiooni",
+ "tog-editrecovery-help": "Saad anda tagasisidet [$1 projekti aruteluleheküljel].",
"underline-always": "Alati",
"underline-never": "Mitte kunagi",
"underline-default": "Kujunduse või brauseri vaikeväärtus",
@@ -651,8 +652,10 @@
"edit-recovery-special-recovered-on": "seisuga $1",
"edit-recovery-special-recovered-on-tooltip": "Kuupäev ja kellaaeg, mil sinu taastatud andmed viimati salvestati",
"edit-recovery-loaded-title": "Muudatused taastatud",
- "edit-recovery-loaded-message": "Sinu salvestamata muudatused on automaatselt taastatud",
- "edit-recovery-loaded-message-different-rev": "Pane tähele, et lehekülge võib olla muudetud pärast seda, kui redigeerimist alustasid",
+ "edit-recovery-loaded-message": "Sinu salvestamata muudatused on automaatselt taastatud.",
+ "edit-recovery-loaded-message-different-rev": "<em>Pane tähele, et lehekülge võib olla muudetud pärast seda, kui redigeerimist alustasid.</em>",
+ "edit-recovery-loaded-message-different-rev-publish": "<em>Palun vaata enda muudatused enne avaldamist üle.</em>",
+ "edit-recovery-loaded-message-different-rev-save": "<em>Palun vaata enda muudatused enne salvestamist üle.</em>",
"edit-recovery-loaded-show": "Näita muudatusi",
"edit-recovery-loaded-discard": "Hülga muudatused",
"summary": "Resümee:",
@@ -775,7 +778,7 @@
"modeleditnotsupported-text": "Sisumudeli $1 redigeerimise tugi puudub.",
"permissionserrors": "Loatõrge",
"permissionserrorstext": "Sul pole õigust seda teha {{PLURAL:$1|järgmisel põhjusel|järgmistel põhjustel}}:",
- "permissionserrorstext-withaction": "Sul pole lubatud {{lcfirst:$2}} {{PLURAL:$1|järgmisel põhjusel|järgmistel põhjustel}}:",
+ "permissionserrorstext-withaction": "Sul pole lubatud $2 {{PLURAL:$1|järgmisel põhjusel|järgmistel põhjustel}}:",
"contentmodelediterror": "Sa ei saa seda redaktsiooni redigeerida, sest selle sisumudel <code>$1</code> erineb lehekülje praegusest sisumudelist <code>$2</code>.",
"recreate-moveddeleted-warn": "'''Hoiatus: Lood uuesti lehekülge, mis on varem kustutatud.'''\n\nKaalu, kas lehekülje uuesti loomine on kohane.\nLehekülje eelnevad kustutamised ja teisaldamised:",
"moveddeleted-notice": "Seda lehekülge pole.\nAllpool on ära toodud selle lehekülje sissekanded teisaldamis-, kaitsmis- ja kustutamislogis.",
@@ -1110,6 +1113,7 @@
"prefs-searchmisc": "Üldine",
"searchprefs": "Otsi eelistusi",
"searchprefs-noresults": "Tulemusi ei leitud.",
+ "searchprefs-results": "$1 {{PLURAL:$1|tulemus|tulemust}}",
"saveprefs": "Salvesta eelistused",
"restoreprefs": "Taasta kõik vaike-eelistused",
"prefs-editing": "Toimetamine",
@@ -1899,6 +1903,12 @@
"uploadstash-errclear": "Failide eemaldamine ebaõnnestus.",
"uploadstash-refresh": "Värskenda faililoendit",
"uploadstash-exception": "Üleslaaditavat faili ei õnnestunud peithoidlas talletada ($1): \"$2\".",
+ "uploadstash-nothumb": "Pisipilt puudub",
+ "uploadstash-header-date": "Kuupäev",
+ "uploadstash-header-filekey": "Võti",
+ "uploadstash-header-thumb": "Pisipilt",
+ "uploadstash-header-dimensions": "Mõõtmed",
+ "uploadstash-pager-submit": "Näita failide loendit",
"uploadstash-bad-path": "Teed pole olemas.",
"uploadstash-bad-path-invalid": "Tee pole sobiv.",
"uploadstash-bad-path-unknown-type": "Tundmatu tüüp \"$1\".",
@@ -2683,7 +2693,7 @@
"ipb-sitewide": "Saidiülene",
"ipb-partial": "Osaline",
"ipb-sitewide-help": "Blokeeritakse lehekülgede redigeerimine kõigis nimeruumides ning ka muud toimingud, mis on muidu lubatud ([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Blocking_users#What_it_means_to_be_blocked loe lähemalt]).",
- "ipb-partial-help": "Saad valida leheküljed ja nimeruumid, mille muutmine keelata. Ei blokeerita kõiki toiminguid, mis on muidu lubatud. Saad keelata teatud toimingud.",
+ "ipb-partial-help": "Blokeeritakse redigeerimine valitud lehekülgedel ja nimeruumides. Ei blokeerita kõiki toiminguid, mis on muidu lubatud ([https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Blocking_users#What_it_means_to_be_blocked loe veel]). Kasuta märkeruute, et blokeerida kindlad toimingud igal leheküljel ja igas nimeruumis.",
"ipb-action-create": "Uute lehekülgede alustamine ja uute failide üleslaadimine",
"ipb-action-move": "Lehekülgede ja failide teisaldamine",
"ipb-action-upload": "Failide üleslaadimine (kaasa arvatud failide ülekirjutamine)",
diff --git a/languages/i18n/ha.json b/languages/i18n/ha.json
index 7b0bb1fe3a08..23dea66743e3 100644
--- a/languages/i18n/ha.json
+++ b/languages/i18n/ha.json
@@ -23,6 +23,7 @@
"Lamma64",
"Mladanali",
"Mojaam",
+ "Muhammad Idriss Criteria",
"Nura A Sani",
"Omar Ali",
"Yusuf Sa'adu",
diff --git a/languages/i18n/he.json b/languages/i18n/he.json
index a3d643e65790..249b2f4cf732 100644
--- a/languages/i18n/he.json
+++ b/languages/i18n/he.json
@@ -1134,6 +1134,7 @@
"prefs-searchmisc": "כללי",
"searchprefs": "חיפוש בהעדפות",
"searchprefs-noresults": "אין תוצאות",
+ "searchprefs-results": "{{PLURAL:$1|תוצאה אחת|$1 תוצאות}}",
"saveprefs": "שמירה",
"restoreprefs": "שחזור הגדרות ברירת המחדל",
"prefs-editing": "עריכה",
diff --git a/languages/i18n/hif-latn.json b/languages/i18n/hif-latn.json
index ce507e3bf028..2276410e32ec 100644
--- a/languages/i18n/hif-latn.json
+++ b/languages/i18n/hif-latn.json
@@ -438,7 +438,7 @@
"noname": "Aap achchha user name ke nai specify karaa hai.",
"loginsuccesstitle": "Login safal bhais",
"loginsuccess": "'''Aap \"$1\" ke naam pe {{SITENAME}} me logged in hai.'''",
- "nosuchuser": "\"$1\" naam ke koi sadasya nai hai.\nSadasya ke naam case sensitive hai.\nAapan spelling check karo nai to [[Special:CreateAccount|nawaa account banao]].",
+ "nosuchuser": "\"$1\" naam ke koi sadasya nai hai.\nSadasya ke naam case-sensitive hai.\nAapan spelling check karo nai to [[Special:CreateAccount|nawaa account banao]].",
"nosuchusershort": "\"$1\" naam ke koi sadasya nai hai.\nAapan spelling check karo.",
"nouserspecified": "Aap ke aapan username de ke parri.",
"login-userblocked": "Ii sadasya ke rok dewa gais hae. Login kare ke ijajat nai hae.",
diff --git a/languages/i18n/hu.json b/languages/i18n/hu.json
index 633fdc4267a1..b25fd88ac919 100644
--- a/languages/i18n/hu.json
+++ b/languages/i18n/hu.json
@@ -600,9 +600,11 @@
"botpasswords-label-delete": "Törlés",
"botpasswords-label-resetpassword": "Új jelszó kérése",
"botpasswords-label-grants": "Elérhető jogosultságok:",
+ "botpasswords-help-grants": "Az engedélyek hozzáférést tesznek lehetővé a felhasználói fiókodban már meglévő jogosulságokhoz. Az engedélyezés itt nem biztosít hozzáférést olyan jogosultságokhoz, amelyekkel a felhasználói fiókod egyébként nem rendelkezne. További információkért tekintsd meg a [[Special:ListGrants|engedélyek táblázatát]].\n\nA kockázatos jogosultságok meg vannak jelölve. A megjelöletlen jogosultságok általában nehéz visszaélni, ha a botot valaki rosszindulatú szándékkal eltéríti. A vandalizmus kockázatával járó jogosultságok (<span class=\"mw-grantriskgroup-vandalism\">{{int:grantriskgroup-vandalism}}</span> ) felhasználhatók olyan zavarok okozására, amelyek tisztítása sok erőfeszítést igényel. A biztonsági kockázattal járó jogosultságok (<span class=\"mw-grantriskgroup-security\">{{int:grantriskgroup-security}}</span> ) maradandó károkat okozhatnak (például személyes adatok ellopását). A belső jogosultságokat (<span class=\"mw-grantriskgroup-internal\">{{int:grantriskgroup-internal}}</span>) jellemzően csak a webhely operátora használja. Kérjük, ne kérj kockázatos jogosultságokat, hacsak nincs rájuk valóban szükséged.",
"botpasswords-label-grants-column": "Megadva",
"botpasswords-bad-appid": "A(z) „$1” botnév érvénytelen.",
"botpasswords-toolong-restrictions": "Túl sok IP-cím vagy -tartomány van megadva.",
+ "botpasswords-toolong-grants": "Túl sok jogosultság van kiválasztva.",
"botpasswords-insert-failed": "A(z) „$1” botnév hozzáadása sikertelen. Nem lehet, hogy már hozzá lett adva?",
"botpasswords-update-failed": "A(z) „$1” nevű botfiók frissítése sikertelen. Lehet, hogy törölted?",
"botpasswords-created-title": "Botjelszó létrehozva",
@@ -675,9 +677,11 @@
"edit-recovery-special-intro-empty": "Nincsenek mentetlen módosításaid.",
"edit-recovery-special-view": "megtekintés",
"edit-recovery-special-edit": "szerkesztés",
+ "edit-recovery-special-recovered-on-tooltip": "A helyreállítási adatok utolsó mentésének dátuma és időpontja",
"edit-recovery-loaded-title": "Változtatások helyreállítva",
- "edit-recovery-loaded-message": "Az elmentetlen változtatásaidat automatikusan helyreállítottuk",
+ "edit-recovery-loaded-message": "Az elmentetlen változtatásaidat automatikusan helyreállítottuk.",
"edit-recovery-loaded-message-different-rev": "Vedd figyelembe, hogy az oldal változhatott a szerkesztés megkezdése óta Mentés előtt tekintsd át a módosításokat",
+ "edit-recovery-loaded-message-different-rev-save": "<em>Mentés előtt tekintsd át a módosításokat.</em>",
"edit-recovery-loaded-show": "Változtatások megtekintése",
"edit-recovery-loaded-discard": "Változtatások elvetése",
"summary": "Összefoglaló:",
@@ -992,7 +996,7 @@
"mergehistory-go": "Egyesíthető szerkesztések mutatása",
"mergehistory-submit": "Változatok egyesítése",
"mergehistory-empty": "Nincs egyesíthető változás.",
- "mergehistory-done": "$1 $4 változata sikeresen egyesítve lett a(z) [[:$2]] lappal.\n$3.",
+ "mergehistory-done": "$1 $4 változata sikeresen egyesítve lett a(z) [[:$2]] lappal.\n$3",
"mergehistory-fail": "Nem sikerült a laptörténetek egyesítése. Kérlek, ellenőrizd újra az oldalt és az időparamétereket.",
"mergehistory-fail-bad-timestamp": "Érvénytelen időbélyeg.",
"mergehistory-fail-invalid-source": "Érvénytelen forráslap.",
@@ -1141,6 +1145,7 @@
"recentchangesdays-max": "Legfeljebb $1 {{PLURAL:$1|nap}}",
"recentchangescount": "Az alapértelmezettként mutatott szerkesztések száma a friss változtatásoknál, laptörténetekben és naplókban:",
"prefs-back-title": "Vissza a beállításokhoz",
+ "prefs-description-personal": "Szabályozd, hogyan jelenjél meg, hogyan csatlakozzál és kommunikálj.",
"prefs-description-rendering": "A felület, méret és olvasási beállítások módosítása.",
"prefs-description-editing": "A szerkesztések végzésének, követésének és ellenőrzésének testreszabása.",
"prefs-description-rc": "A friss változtatások listájának testreszabása.",
@@ -1183,6 +1188,7 @@
"prefs-files": "Fájlok",
"prefs-custom-css": "saját CSS",
"prefs-custom-js": "saját JavaScript",
+ "prefs-custom-cssjs-safemode": "Az egyéni CSS/JavaScript jelenleg nem érhető el, mivel a biztonsági mód engedélyezve van. Kérjük, [[#mw-input-wpforcesafemode|kapcsold ki a biztonsági módot]] az egyéni CSS/JavaScript használatához.",
"prefs-common-config": "Közös CSS/JavaScript minden skinhez:",
"prefs-reset-intro": "Ezen a lapon állíthatod vissza a beállításaidat az oldal alapértelmezett értékeire.\nA műveletet nem lehet visszavonni.",
"prefs-reset-confirm": "Igen, vissza akarom állítani a beállításaimat.",
@@ -1201,17 +1207,22 @@
"badsig": "Érvénytelen aláírás; ellenőrizd a HTML-formázást.",
"badsightml": "Az aláírásod érvénytelen vagy elavult HTML-szintaxist tartalmaz:",
"badsiglinks": "Aláírásodnak tartalmaznia kell egy linket felhasználói lapodra, vitalapodra vagy közreműködéseidre ezen a wikin. Adj meg legalább egy linket, például a következő módon: <code>$1</code>.",
+ "badsigsubst": "Az aláírása beágyazott helyettesítést tartalmaz (pl. <code>subst:</code> vagy <code><nowiki>~~~~</nowiki></code>).",
"badsiglength": "Az aláírásod túl hosszú.\nLegfeljebb $1 {{PLURAL:$1|karakteres}} lehet.",
"badsiglinebreak": "Az aláírásodnak egyetlen sornyi wikiszövegből kell állnia.",
"linterror-bogus-image-options": "Hamis fájl opció",
"linterror-big-tables": "Nagy táblázatok, amelyek megtörik a kijelzőt a mobileszközökön",
"linterror-deletable-table-tag": "Törlendő táblázatcímke",
+ "linterror-html5-misnesting": "Hibás beágyazás (most hibás)",
"linterror-misc-tidy-replacement-issues": "Különféle jelölési hibák",
+ "linterror-misnested-tag": "Rosszul beágyazott címke, amelyet megfelelően be kell ágyazni",
"linterror-missing-end-tag": "Hiányzik a végcímke",
"linterror-multiline-html-table-in-list": "Többsoros HTML5-táblázat egy listában belül",
"linterror-multiple-unclosed-formatting-tags": "Több lezáratlan formázási cimke egy oldalon",
"linterror-obsolete-tag": "Elavult HTML-címke",
+ "linterror-pwrap-bug-workaround": "Bekezdéstördelési hiba megoldása",
"linterror-self-closed-tag": "Önzáró címke",
+ "linterror-stripped-tag": "Csupaszított címke",
"linterror-tidy-font-bug": "A <font> címke módosítani próbálja a linkszöveg színét",
"linterror-unclosed-quotes-in-heading": "Lezáratlan idézet, amely kiszivárog a tartalomjegyzékből",
"yourgender": "Milyen neműként hivatkozzunk rád?",
@@ -1254,6 +1265,7 @@
"prefs-diffs": "Eltérések (diffek)",
"prefs-help-prefershttps": "A beállítás a legközelebbi belépés után lép érvénybe.",
"prefs-tabs-navigation-hint": "Tipp: a jobbra-balra nyilakkal navigálhatsz a fülek között.",
+ "prefs-sections-navigation-hint": "Tipp: A Tab és a Shift+Tab billentyűk segítségével navigálhatsz a preferencia szakaszok között.",
"userrights": "Szerkesztői jogok",
"userrights-lookup-user": "Válasszon ki egy felhasználót",
"userrights-user-editname": "Add meg a szerkesztő nevét:",
@@ -1598,6 +1610,7 @@
"rcfilters-filter-user-experience-level-registered-description": "Bejelentkezett szerkesztők.",
"rcfilters-filter-user-experience-level-unregistered-label": "Nem regisztrált",
"rcfilters-filter-user-experience-level-unregistered-description": "Nem bejelentkezett szerkesztők.",
+ "rcfilters-filter-user-experience-level-unregistered-description-temp": "Szerkesztők, akik nincsenek bejelentkezve, és olyan szerkesztők, akik ideiglenes fiókokat használnak.",
"rcfilters-filter-user-experience-level-newcomer-label": "Újoncok",
"rcfilters-filter-user-experience-level-newcomer-description": "Regisztrált szerkesztők kevesebb mint 10 szerkesztéssel vagy 4 nap aktivitással.",
"rcfilters-filter-user-experience-level-learner-label": "Tanulók",
@@ -1641,7 +1654,7 @@
"rcfilters-filter-categorization-label": "Kategóriaváltoztatások",
"rcfilters-filter-categorization-description": "Bejegyzések lapok kategóriákhoz való hozzáadásáról vagy azokból való eltávolításáról.",
"rcfilters-filter-logactions-label": "Naplózott műveletek",
- "rcfilters-filter-logactions-description": "Adminisztratív műveletek, fióklétrehozások, laptörlések, feltöltések…",
+ "rcfilters-filter-logactions-description": "Adminisztratív műveletek, laptörlések, feltöltések…",
"rcfilters-filter-accountcreations-label": "Fióklétrehozások",
"rcfilters-filter-accountcreations-description": "Fióklétrehozásról szóló naplóbejegyzések. Be kell hozzá kapcsolni a naplózott műveleteket.",
"rcfilters-hideminor-conflicts-typeofchange-global": "Az „apró változtatás” szűrő ütközik egy vagy több „változtatás típusa” szűrővel, mert bizonyos változtatástípusok nem jelölhetők aprónak. Az ütköző szűrők meg vannak jelölve a fenti Aktív szűrők részen.",
@@ -1911,6 +1924,12 @@
"uploadstash-errclear": "A fájlok törlése nem sikerült.",
"uploadstash-refresh": "Fájlok listájának frissítése",
"uploadstash-exception": "Nem sikerült eltárolni a feltöltést a stash-ben ($1): „$2”",
+ "uploadstash-nothumb": "Nincs bélyegkép",
+ "uploadstash-header-date": "Dátum",
+ "uploadstash-header-filekey": "Kulcs",
+ "uploadstash-header-thumb": "Bélyegkép",
+ "uploadstash-header-dimensions": "Méretek",
+ "uploadstash-pager-submit": "Fájllista megjelenítése",
"uploadstash-bad-path": "Útvonal nem létezik",
"uploadstash-bad-path-invalid": "Érvénytelen útvonal",
"uploadstash-bad-path-unknown-type": "Ismeretlen típus: „$1”",
@@ -2218,6 +2237,8 @@
"apisandbox-request-format-php-label": "PHP-tömb",
"apisandbox-request-php-label": "A kérés PHP-tömbként:",
"apisandbox-request-time": "Kérés hossza: $1 ms",
+ "apisandbox-request-post": "Ezt a kérést a HTTP POST módszerrel kell elküldeni.",
+ "apisandbox-request-formdata": "Ezt a kérést fájlfeltöltésként kell elküldeni (azaz <code>multipart/form-data</code> használatával).",
"apisandbox-results-fixtoken": "Token javítása és újrapróbálkozás",
"apisandbox-results-fixtoken-fail": "A(z) „$1” token lekérése sikertelen.",
"apisandbox-alert-page": "Hibás mezők vannak ezen a lapon.",
@@ -2428,6 +2449,7 @@
"enotif_body_intro_changed": "$2 megváltoztatta a(z) $1 {{SITENAME}}-oldalt $PAGEEDITDATE dátumon, az aktuális verziót lásd a $3 címen.",
"enotif_lastvisited": "Az utolsó látogatásod óta történt változtatásokért lásd: $1",
"enotif_lastdiff": "Ezen változtatás megtekintéséhez lásd: $1",
+ "enotif_pagelog": "Az oldal naplójának megtekintéséhez lásd: $1",
"enotif_anon_editor": "$1 névtelen felhasználó",
"enotif_temp_editor": "ideiglenes felhasználó $1",
"enotif_body": "Kedves $WATCHINGUSERNAME!\n\n$PAGEINTRO $NEWPAGE $PAGELOG\n\nA szerkesztési összefoglaló a következő volt: $PAGESUMMARY $PAGEMINOREDIT\n\nA szerkesztő elérhetősége:\ne-mail küldése: $PAGEEDITOR_EMAIL\nwiki: $PAGEEDITOR_WIKI\n\nAmíg nem keresed fel az oldalt bejelentkezve, addig nem érkeznek újabb értesítések az oldal változásaival kapcsolatban. A figyelőlistádon is beállíthatod, hogy újból kapj értesítéseket, az összes lap után.\n\nBaráti üdvözlettel: a(z) {{SITENAME}} értesítő rendszere\n\n--\nAz e-mail-értesítéseid módosításához keresd fel a \n{{canonicalurl:{{#special:Preferences}}}} címet\n\nA figyelőlistád módosításához keresd fel a\n{{canonicalurl:{{#special:EditWatchlist}}}} címet\n\nA lap figyelőlistádról való törléséhez keresd fel a\n$UNWATCHURL címet\n\nVisszajelzés és további segítség:\n$HELPPAGE",
@@ -2482,8 +2504,8 @@
"cantrollback": "Nem lehet visszaállítani.\nAz utolsó szerkesztést végző felhasználó a lap egyetlen szerzője.",
"alreadyrolled": "[[:$1]] utolsó, [[User:$2|$2]] ([[User talk:$2|vita]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) általi szerkesztését nem lehet visszavonni.\nIdőközben valaki már visszavonta, vagy szerkesztette a lapot.\n\nAz utolsó szerkesztést [[User:$3|$3]] ([[User talk:$3|vita]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) végezte.",
"editcomment": "A szerkesztési összefoglaló <em>$1</em> volt.",
- "revertpage": "Visszaállítottam a lap korábbi változatát [[Special:Contributions/$2|$2]] ([[User talk:$2|vita]]) szerkesztéséről [[User:$1|$1]] szerkesztésére",
- "revertpage-anon": "Visszaállítottam a lap korábbi változatát [[Special:Contributions/$2|$2]] szerkesztéséről [[User:$1|$1]] szerkesztésére",
+ "revertpage": "Visszaállítottam a {{PLURAL:$7|lap}} korábbi változatát [[Special:Contributions/$2|$2]] ([[User talk:$2|vita]]) szerkesztéséről [[User:$1|$1]] szerkesztésére",
+ "revertpage-anon": "Visszaállítottam a {{PLURAL:$7|lap}} korábbi változatát [[Special:Contributions/$2|$2]] szerkesztéséről [[User:$1|$1]] szerkesztésére",
"revertpage-nouser": "Visszaállítottam a lap korábbi változatát (szerkesztőnév eltávolítva) szerkesztéséről [[User:$1|$1]] szerkesztésére",
"rollback-success": "{{GENDER:$3|$1}} szerkesztéseit visszaállítottam {{GENDER:$4|$2}} utolsó változatára.",
"sessionfailure-title": "Munkamenethiba",
@@ -3263,6 +3285,7 @@
"confirmemail_oncreate": "A megerősítő kódot elküldtük az e-mail-címedre.\nEz a kód nem szükséges a belépéshez, de meg kell adnod,\nmielőtt a wiki e-mail-alapú szolgáltatásait igénybe veheted.",
"confirmemail_sendfailed": "Nem sikerült elküldeni a megerősítő e-mailt.\nEllenőrizd, hogy nem írtál-e érvénytelen karaktert a címbe.\n\nA levelező üzenete: $1",
"confirmemail_invalid": "Nem megfelelő kód.\nLehet, hogy a kód felhasználhatósági ideje lejárt.",
+ "confirmemail_invalid_format": "Érvénytelen megerősítő kód.\nEllenőrizd az e-mailed, előfordulhat, hogy a megerősítő linket az e-mail kliens csonkolta.",
"confirmemail_needlogin": "$1 az e-mail-címed megerősítéséhez.",
"confirmemail_success": "Az e-mail-címed megerősítve. Most már [[Special:UserLogin|beléphetsz a wikibe]].",
"confirmemail_loggedin": "Az e-mail-címed megerősítve.",
@@ -3281,6 +3304,7 @@
"scarytranscludefailed-httpstatus": " [Nem sikerült betölteni a(z) $1 sablont: HTTP $2]",
"scarytranscludetoolong": "[Az URL túl hosszú]",
"deletedwhileediting": "'''Figyelmeztetés:''' A lapot a szerkesztés megkezdése után törölték!",
+ "editedwhiledeleting": "A törölni kívánt oldalt szerkesztették. Kérjük, erősítsd meg újra a törlést.",
"confirmrecreate": "Miután elkezdted szerkeszteni, [[User:$1|$1]] ([[User talk:$1|vita]]) törölte ezt a lapot a következő indokkal:\n: <em>$2</em>\nKérlek erősítsd meg, hogy tényleg újra létre akarod-e hozni a lapot.",
"confirmrecreate-noreason": "[[User:$1|$1]] ([[User talk:$1|vita]]) törölte ezt a lapot, miután elkezdted szerkeszteni. Erősítsd meg, hogy tényleg ismét létre szeretnéd hozni a lapot.",
"recreate": "Újraírás",
@@ -3455,6 +3479,8 @@
"specialpages-group-developer": "Fejlesztői eszközök",
"blankpage": "Üres lap",
"intentionallyblankpage": "Ez a lap szándékosan maradt üresen",
+ "iframeerror-title": "Iframe hiba",
+ "iframeerror-message": "Ez a tartalom nem jeleníthető meg, mert böngésződ nem támogatja a [https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/srcdoc iframe srcdoc attribútumot].",
"disabledspecialpage-disabled": "Ezt az oldalt egy rendszer-adminisztrátor letiltotta.",
"external_image_whitelist": " #Ezt a sort hagyd pontosan így, ahogy van<pre>\n#Ide reguláris kifejezéseket írhatsz (azon részüket, amik a // közé mennek)\n#Ezek egyeztetve lesznek a külső képek URL-jeivel\n#Egyezés esetén képként fognak megjelenni, egyébként csak link fog rájuk mutatni\n#A #-tel kezdődő sorok megjegyzésnek számítanak\n#A kis- és nagybetűk nincsenek megkülönböztetve\n\n#A reguláris kifejezéseket ezen sor alá írd. Ezt a sort hagyd így, ahogy van.</pre>",
"tags": "Lapváltozat-címkék",
@@ -3904,6 +3930,7 @@
"mw-widgets-titlesmultiselect-placeholder": "Továbbiak hozzáadása…",
"date-range-from": "Dátumtól:",
"date-range-to": "Dátumig:",
+ "session-page-restricted": "Nem vagy jogosult az oldal szerkesztésére a jelenlegi jogosultságokkal",
"sessionmanager-tie": "Nem kombinálható többféle hitelesítési típus: $1.",
"sessionprovider-generic": "$1-munkamenetek",
"sessionprovider-mediawiki-session-cookiesessionprovider": "sütialapú munkamenetek",
@@ -4049,7 +4076,7 @@
"specialmute-error-mutelist-disabled": "A némítási funkciók nem elérhetők, mert a wiki rendszergazdája letiltotta az e-mail némítási funkciókat.",
"specialmute-error-no-email-set": "A némítási funkciók nem elérhetők, mert nem erősítetted meg az e-mail címed.",
"specialmute-error-no-options": "A némítási funkciók nem elérhetők.",
- "specialmute-email-footer": "{{BIDI:$2}} e-mail-beállításainak kezeléséhez látogasd meg a(z) <$1> oldalt.",
+ "specialmute-email-footer": "{{BIDI:$2}} e-mail-beállításainak kezeléséhez látogasd meg az alábbi URL-t:\n<$1>",
"specialmute-login-required": "Kérjük, jelentkezz be a némítási beállításaid módosításához.",
"mute-preferences": "Némítási beállítások",
"revid": "$1 változat",
@@ -4105,6 +4132,7 @@
"skin-action-undelete": "Visszaállítás",
"skin-action-delete": "Törlés",
"skin-action-move": "Átnevezés",
+ "parsoid-stash-rate-limit-error": "A tárolas nem sikerült, mert túllépted a korlátot. Kérlek, próbáld újra később.",
"parsoid-resource-limit-exceeded": "A Parsoid-erőforráslimit túllépve: $1",
"parsoid-revision-access": "A Parsoid nem fér hozzá a következő változathoz: $1",
"parsoid-client-error": "Érvénytelen Parsoid-bemenet: $1",
diff --git a/languages/i18n/ia.json b/languages/i18n/ia.json
index 118049f8972f..97c78fd97bc3 100644
--- a/languages/i18n/ia.json
+++ b/languages/i18n/ia.json
@@ -67,6 +67,7 @@
"tog-requireemail": "Inviar messages pro reinitialisar contrasigno solmente quando le adresse de e-mail assi como le nomine de usator es fornite.",
"tog-forcesafemode": "Sempre activar le [[mw:Manual:Safemode|modo secur]]",
"tog-editrecovery": "Activar le function [[Special:EditRecovery|{{int:editrecovery}}]]",
+ "tog-editrecovery-help": "Tu pote lassar tu opinion sur le [$1 pagina de discussion del projecto].",
"underline-always": "Sempre",
"underline-never": "Nunquam",
"underline-default": "Como definite per tu navigator o apparentia",
@@ -635,6 +636,8 @@
"edit-recovery-loaded-title": "Cambiamentos recuperate",
"edit-recovery-loaded-message": "Tu cambiamentos non salveguardate ha essite automaticamente recuperate.",
"edit-recovery-loaded-message-different-rev": "<em>Nota ben que le pagina pote haber cambiate post que tu ha comenciate a modificar lo.</em>",
+ "edit-recovery-loaded-message-different-rev-publish": "<em>Per favor revide tu modificationes ante de publicar.</em>",
+ "edit-recovery-loaded-message-different-rev-save": "<em>Per favor revide tu modificationes ante de salveguardar.</em>",
"edit-recovery-loaded-show": "Detaliar modificationes",
"edit-recovery-loaded-discard": "Abandonar modificationes",
"summary": "Summario:",
@@ -1092,6 +1095,7 @@
"prefs-searchmisc": "General",
"searchprefs": "Cercar preferentias",
"searchprefs-noresults": "Nulle resultato",
+ "searchprefs-results": "$1 resultato{{PLURAL:$1||s}}",
"saveprefs": "Confirmar",
"restoreprefs": "Restaurar tote le parametros predefinite",
"prefs-editing": "Modification",
@@ -1881,6 +1885,12 @@
"uploadstash-errclear": "Le radimento del files ha fallite.",
"uploadstash-refresh": "Refrescar le lista de files",
"uploadstash-exception": "Impossibile immagazinar le incargamento in le reserva ($1): “$2”.",
+ "uploadstash-nothumb": "Nulle miniatura",
+ "uploadstash-header-date": "Data",
+ "uploadstash-header-filekey": "Clave",
+ "uploadstash-header-thumb": "Miniatura",
+ "uploadstash-header-dimensions": "Dimensiones",
+ "uploadstash-pager-submit": "Monstrar lista de files",
"uploadstash-bad-path": "Le percurso non existe.",
"uploadstash-bad-path-invalid": "Le cammino non es valide.",
"uploadstash-bad-path-unknown-type": "Typo “$1” incognite.",
diff --git a/languages/i18n/inh.json b/languages/i18n/inh.json
index 5a192b4e9c9c..d8acd8cd344b 100644
--- a/languages/i18n/inh.json
+++ b/languages/i18n/inh.json
@@ -142,7 +142,7 @@
"noindex-category": "Индекс ца оттаю оагӀонаш",
"broken-file-category": "Болх ца бу файлови тӀатовжамаш дола оагӀонаш",
"about": "Йоазонца сурт оттадар",
- "article": "Статья",
+ "article": "Лустам",
"newwindow": "(керда кора чу)",
"cancel": "Юхадаккха",
"moredotdotdot": "ДӀахо...",
@@ -290,7 +290,7 @@
"sort-ascending": "Лостам бе доккхахиларгахьа",
"sort-initial": "Юххьанцара лостам/дӀахьанийсдар",
"sort-rowspan-error": "Укх оагӀона чухьа rowspan яха атрибут йолаш таблица йоалл. Цу таблица чу болчул совгӀа мугӀарашка яржаш (уж чуозаш) я из атрибут. Из нийсде йиш я хьа оагӀув тоаярца.",
- "nstab-main": "Статья",
+ "nstab-main": "Лустам",
"nstab-user": "{{GENDER:$1|Доакъашхо}}",
"nstab-media": "Мультимедиа",
"nstab-special": "ГӀулакха оагӀув",
@@ -2008,7 +2008,7 @@
"unusedtemplates": "Леладеш доаца лераш",
"unusedtemplatestext": "Укхаза дагаръяьй «{{ns:template}}» яхача цӀерий арен массайола оагӀонаш, кхыча оагӀонашта юкъеяьха йоацаш. Ло дӀабаккхалехь цунна тӀахьожавеш тӀатовжамаш доацалга тахка диц ма де.",
"unusedtemplateswlh": "кхыдола тӀатовжамаш",
- "randompage": "Цаховш нийсъенна статья",
+ "randompage": "Цаховш нийсъенна оагӀув",
"randompage-nopages": "ТӀехьайоагӀача цӀерий {{PLURAL:$2|1=ара|аренашка}} оагӀонаш яц: $1.",
"randomincategory": "Цаховш оагӀув оагӀата чу",
"randomincategory-invalidcategory": "ОагӀат «$1» йолаш яц.",
@@ -2355,7 +2355,7 @@
"watchthispage": "Зем бе укх оагӀонна",
"unwatch": "Ма бе зем",
"unwatchthispage": "Зем бар соцаде",
- "notanarticle": "Статья яц",
+ "notanarticle": "Лустам бац",
"notvisiblerev": "ОагӀон эрш дӀадаьккхад",
"watchlist-details": "Хьа зем-листама чу $1 {{PLURAL:$1|оагӀув}} я (иштта {{PLURAL:$1|къамаьла оагӀонаш}} а).",
"watchlist-expiry-days-left": "{{PLURAL:$1|$1 ди}} дисад",
diff --git a/languages/i18n/it.json b/languages/i18n/it.json
index df4daa684213..b09ce401467d 100644
--- a/languages/i18n/it.json
+++ b/languages/i18n/it.json
@@ -1206,6 +1206,7 @@
"prefs-searchmisc": "Generale",
"searchprefs": "Cerca preferenze",
"searchprefs-noresults": "Nessun risultato",
+ "searchprefs-results": "$1 {{PLURAL:$1|risultato|risultati}}",
"saveprefs": "Salva",
"restoreprefs": "Ripristina tutte le impostazioni predefinite",
"prefs-editing": "Casella di modifica",
diff --git a/languages/i18n/mk.json b/languages/i18n/mk.json
index 032b314ed7be..50a2ab0712f1 100644
--- a/languages/i18n/mk.json
+++ b/languages/i18n/mk.json
@@ -1112,6 +1112,7 @@
"prefs-searchmisc": "Општи",
"searchprefs": "Нагодувања на пребарувањето",
"searchprefs-noresults": "Нема исход",
+ "searchprefs-results": "$1 {{PLURAL:$1|ставка|ставки}}",
"saveprefs": "Зачувај",
"restoreprefs": "Врати сè по основно",
"prefs-editing": "Уредување",
diff --git a/languages/i18n/nb.json b/languages/i18n/nb.json
index 5ba5262796d2..ce26c7b9ea8d 100644
--- a/languages/i18n/nb.json
+++ b/languages/i18n/nb.json
@@ -1142,6 +1142,7 @@
"prefs-searchmisc": "Generelt",
"searchprefs": "Søk i innstillingene",
"searchprefs-noresults": "Ingen resultater",
+ "searchprefs-results": "$1 {{PLURAL:$1|resultat|resultater}}",
"saveprefs": "Lagre",
"restoreprefs": "Nullstill alt til standardinnstillinger",
"prefs-editing": "Redigering",
diff --git a/languages/i18n/nl.json b/languages/i18n/nl.json
index 99c1a8997226..08fdfdb01cf8 100644
--- a/languages/i18n/nl.json
+++ b/languages/i18n/nl.json
@@ -1193,6 +1193,7 @@
"prefs-searchmisc": "Algemeen",
"searchprefs": "Voorkeuren doorzoeken",
"searchprefs-noresults": "Geen resultaten",
+ "searchprefs-results": "$1 {{PLURAL:$1|resulaat|resultaten}}",
"saveprefs": "Opslaan",
"restoreprefs": "Alle standaardinstellingen terugzetten",
"prefs-editing": "Bewerken",
diff --git a/languages/i18n/nso.json b/languages/i18n/nso.json
index 2259518bac3e..7d6def3674bd 100644
--- a/languages/i18n/nso.json
+++ b/languages/i18n/nso.json
@@ -243,7 +243,7 @@
"noname": "Gawa fana ka leina la mošomiši la go loka.",
"loginsuccesstitle": "O tsene",
"loginsuccess": "'''Bjale o tsene go {{SITENAME}} bjalo ka \"$1\".'''",
- "nosuchuser": "Ga gona mošomiši wa leina la \"$1\".\nMaina a huduetša ke seemo sa ditlhaka (case sensitive).\nLebele mopeleto wa gago goba [[Special:CreateAccount|o tlhome mošomiši yo mophsa]].",
+ "nosuchuser": "Ga gona mošomiši wa leina la \"$1\".\nMaina a huduetša ke seemo sa ditlhaka (case-sensitive).\nLebele mopeleto wa gago goba [[Special:CreateAccount|o tlhome mošomiši yo mophsa]].",
"nosuchusershort": "Ga gona mošomiši wa leina la \"$1\". Hlokomela mopeleto wa gago.",
"nouserspecified": "O swanela ke go fana ka leina la mošomiši.",
"wrongpassword": "O loketše ditlhaka-tša-siphiri tšeo e sego tšona. Ka kgopelo, leka gape.",
diff --git a/languages/i18n/pl.json b/languages/i18n/pl.json
index e33eccaeefd2..50a41b0c2069 100644
--- a/languages/i18n/pl.json
+++ b/languages/i18n/pl.json
@@ -1191,6 +1191,7 @@
"prefs-searchmisc": "Ogólne",
"searchprefs": "Wyszukaj ustawienie",
"searchprefs-noresults": "Brak wyników",
+ "searchprefs-results": "$1 {{PLURAL:$1|wynik|wyniki|wyników}}",
"saveprefs": "Zapisz",
"restoreprefs": "Przywróć wszystkie domyślne preferencje",
"prefs-editing": "Edycja",
diff --git a/languages/i18n/pnb.json b/languages/i18n/pnb.json
index 23880e4b8099..ed21008589bf 100644
--- a/languages/i18n/pnb.json
+++ b/languages/i18n/pnb.json
@@ -126,7 +126,7 @@
"period-pm": "PM",
"pagecategories": "{{PLURAL:$1|گٹھ|گٹھاں}}",
"category_header": "{{پنجابی}}گٹھ \"$1\" وچ صفحے",
- "subcategories": "{{پنجابی}}ذیلی گٹھاں",
+ "subcategories": "ذیلی گٹھاں",
"category-media-header": "ایس \"$1\" گٹھ وچ میڈيا",
"category-empty": "''ایس گٹھ وچ کوئی صفحہ یا میڈیا نہیں۔''",
"hidden-categories": "{{PLURAL:$1|لُکویں گٹھ|لُکویاں گٹھاں}}",
diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json
index d55c5904d6fb..0f7af3843a01 100644
--- a/languages/i18n/qqq.json
+++ b/languages/i18n/qqq.json
@@ -1411,7 +1411,8 @@
"prefs-rendering": "Title of tab in [[Special:Preferences]].\n{{Identical|Appearance}}",
"prefs-searchmisc": "Title of general search options section in search tab",
"searchprefs": "'''Note that this means \"Search in the preferences\" and not \"Preferences of searching\".'''\n\nPlaceholder for the input users can use to search the preferences page.",
- "searchprefs-noresults": "Message showing no results found for searching preferences page.",
+ "searchprefs-noresults": "Message used when no results are found when searching the preferences page.",
+ "searchprefs-results": "Message used when results are found when searching the preferences page. Parameters:\n* $1 - number of results",
"saveprefs": "Button for saving changes in the preferences page.\n\nSee also:\n* {{msg-mw|Saveprefs}}\n* {{msg-mw|Accesskey-preferences-save}}\n* {{msg-mw|Tooltip-preferences-save}}\n{{Identical|Save}}",
"restoreprefs": "Used as label for the Submit button in [[Special:Preferences/reset]].",
"prefs-editing": "Title of a tab in [[Special:Preferences]].\n{{Identical|Editing}}",
@@ -1860,7 +1861,7 @@
"rcfilters-hours-title": "Title for the options to change the number of hours for the results shown.",
"rcfilters-days-show-days": "Title for the button that opens the operation to control the day range for the results. \n\nParameters: $1 - Number of days shown\n{{Identical|Day}}",
"rcfilters-days-show-hours": "Title for the button that opens the operation to control the hour range for the results. \n\nParameters: $1 - Number of hours shown\n{{Identical|Hour}}",
- "rcfilters-highlighted-filters-list": "Text for the tooltip that is displayed over highlighted results, specifying which filters the result matches in [[Special:RecentChanges]] when RCFilters are enabled. \n\nParameters: $1 - A comma separated list of matching filter names.",
+ "rcfilters-highlighted-filters-list": "Text for the tooltip that is displayed over highlighted results, specifying which filters the result matches in [[Special:RecentChanges]] when RCFilters are enabled. \n\nParameter: $1 - A comma-separated list of matching filter names.",
"rcfilters-quickfilters": "Label for the button that opens the saved filter settings menu in [[Special:RecentChanges]]",
"rcfilters-quickfilters-placeholder-title": "Title for the text shown in the quick filters menu on [[Special:RecentChanges]] if the user has not saved any quick filters.",
"rcfilters-quickfilters-placeholder-description": "Description for the text shown in the quick filters menu on [[Special:RecentChanges]] if the user has not saved any quick filters.\n\n\"Active filters\" is {{msg-mw|Rcfilters-activefilters}}.",
@@ -4433,7 +4434,7 @@
"logentry-protect-modify-cascade": "{{Logentry|[[Special:Log/protect]]}}\n\n* $4 - protect expiry (formatted with {{msg-mw|protect-summary-desc}}, multiple possible)\nFor word \"cascading\" see {{msg-mw|protect-summary-cascade}}",
"logentry-rights-rights": "* $1 - (see below)\n* $2 - (see below)\n* $3 - target user, like $1\n* $4 - list of user groups or {{msg-mw|Rightsnone}}\n* $5 - list of user groups or {{msg-mw|Rightsnone}}\n* $6 - target user, can be used with GENDER\n----\n{{Logentry|[[Special:Log/rights]]}}",
"logentry-rights-rights-legacy": "* $1 - username\n* $2 - (see below)\n* $3 - username\n----\n{{Logentry|[[Special:Log/rights]]}}",
- "logentry-rights-autopromote": "* $1 - username\n* $2 - (see below)\n* $3 - (see below) - unused\n* $4 - comma separated list of old user groups or {{msg-mw|Rightsnone}}\n* $5 - comma separated list of new user groups\n* $6 - target user, can be used with GENDER for $3 - unused\n----\n{{Logentry|[[Special:Log/rights]]}}",
+ "logentry-rights-autopromote": "* $1 - username\n* $2 - (see below)\n* $3 - (see below) - unused\n* $4 - comma-separated list of old user groups or {{msg-mw|Rightsnone}}\n* $5 - comma-separated list of new user groups\n* $6 - target user, can be used with GENDER for $3 - unused\n----\n{{Logentry|[[Special:Log/rights]]}}",
"logentry-upload-upload": "{{Logentry|[[Special:Log/upload]]}}",
"logentry-upload-overwrite": "{{Logentry|[[Special:Log/upload]]}}",
"logentry-upload-revert": "{{Logentry|[[Special:Log/upload]]}}",
@@ -4571,7 +4572,7 @@
"mediastatistics-bytespertype": "Combined space of one section of [[Special:MediaStatistics]]. \n*$1 - total space in bytes\n*$2 - total space in \"human units\" (i.e. KB, MB, GB, etc)\n*$3 - What percentage of the space all uploads take up does this file take up.\n*$4 - total count of files\n*$5 - What percentage of all files are in this type.",
"mediastatistics-allbytes": "Combined space of all uploaded files. \n*$1 - total space in bytes\n*$2 - total space in \"human units\" (i.e. KB, MB, GB, etc)\n*$3 - total count of files",
"mediastatistics-table-mimetype": "Header for table on Special:MediaStatistics. Column that lists MIME types (The values in this column will look like 'image/jpeg', and be linked to Special:MIMESearch).",
- "mediastatistics-table-extensions": "Header for column in tables on [[Special:MediaStatistics]] that lists possible extensions for a given file type. (The values in this column will be a comma separated list of file extensions, such as '.webm' or '.png, .apng').",
+ "mediastatistics-table-extensions": "Header for column in tables on [[Special:MediaStatistics]] that lists possible extensions for a given file type. (The values in this column will be a comma-separated list of file extensions, such as '.webm' or '.png, .apng').",
"mediastatistics-table-count": "Column header on Special:MediaStatistics for the number of files column. The headers in this column use {{msg-mw|mediastatistics-nfiles}}.",
"mediastatistics-table-totalbytes": "Column header on Special:MediaStatistics for the number of bytes that this file type takes up. Values for this column use {{msg-mw|mediastatistics-nbytes}}",
"mediastatistics-header-unknown": "Header on Special:MediaStatistics for file types that are in the unknown category\n{{Identical|Unknown}}",
diff --git a/languages/i18n/ru.json b/languages/i18n/ru.json
index 32c324bce31d..f766cd57289e 100644
--- a/languages/i18n/ru.json
+++ b/languages/i18n/ru.json
@@ -2056,6 +2056,12 @@
"uploadstash-errclear": "Очистка файлов не удалась.",
"uploadstash-refresh": "Обновить список файлов",
"uploadstash-exception": "Не удалось сохранить загрузку во временное хранилище ($1): «$2».",
+ "uploadstash-nothumb": "Нет миниатюры",
+ "uploadstash-header-date": "Дата",
+ "uploadstash-header-filekey": "Ключ",
+ "uploadstash-header-thumb": "Миниатюра",
+ "uploadstash-header-dimensions": "Размеры",
+ "uploadstash-pager-submit": "Показать список файлов",
"uploadstash-bad-path": "Путь не существует.",
"uploadstash-bad-path-invalid": "Путь некорректен.",
"uploadstash-bad-path-unknown-type": "Неизвестный тип «$1».",
diff --git a/languages/i18n/sa.json b/languages/i18n/sa.json
index 5e8926bc3085..6c438aa7c8bd 100644
--- a/languages/i18n/sa.json
+++ b/languages/i18n/sa.json
@@ -438,7 +438,7 @@
"noname": "भवता/भवत्या योग्यं सदस्यनाम न प्रदत्तम् ।",
"loginsuccesstitle": "स्वागतं ! प्रवेशः सिद्धः ।",
"loginsuccess": "भवता/भवत्या {{SITENAME}} इत्यत्र \"$1\"-योजकत्वेन प्रवेशः प्राप्तः ।",
- "nosuchuser": "'''$1''' नाम्नः न कोऽपि योजकः विद्यते ।\n\nप्रयोक्तृनामानि पक्षानुगुणं (case sensitive) भवन्ति ।\n\nयत् टङ्कितं, तत् पश्यतु अथवा [[Special:CreateAccount|नूतनसदस्यता प्राप्यताम्]] ।",
+ "nosuchuser": "'''$1''' नाम्नः न कोऽपि योजकः विद्यते ।\n\nप्रयोक्तृनामानि पक्षानुगुणं (case-sensitive) भवन्ति ।\n\nयत् टङ्कितं, तत् पश्यतु अथवा [[Special:CreateAccount|नूतनसदस्यता प्राप्यताम्]] ।",
"nosuchusershort": "'''$1''' नाम्नः न कोऽपि सदस्यः विद्यते ।\n\nयत् टङ्कितं, तत् पश्यतु ।",
"nouserspecified": "भवता/भवत्या एकं योग्यं सदस्यनाम अवश्यमेव दातव्यम् ।",
"login-userblocked": "एषः सदस्यः प्रतिबन्धितः । प्रवेष्टुम् अनुमतिः नास्ति ।",
diff --git a/languages/i18n/sh.json b/languages/i18n/sh.json
index a070a11e7cda..42ddc84e30e9 100644
--- a/languages/i18n/sh.json
+++ b/languages/i18n/sh.json
@@ -31,7 +31,6 @@
]
},
"copyright": "Pod licencom / Под лиценцом: $1.",
- "permissionserrorstext-withaction": "Nemate dopuštenje da $2 iz {{PLURAL:$1|sljedećeg|sljedećih}} razloga:",
"variantname-sh-latn": "Latinica",
"variantname-sh-cyrl": "Ћирилица"
}
diff --git a/languages/i18n/si.json b/languages/i18n/si.json
index 7640099f6643..95dfe407b6e6 100644
--- a/languages/i18n/si.json
+++ b/languages/i18n/si.json
@@ -456,7 +456,7 @@
"noname": "වලංගු පරිශීලක-නාමයක් සඳහන් කිරීමට ඔබ අසමත් වී ඇත.",
"loginsuccesstitle": "ප්‍රවිෂ්ට වී ඇත",
"loginsuccess": "'''දැන් ඔබ , \"$1\" ලෙස, {{SITENAME}} වෙත පිවිස සිටී.'''",
- "nosuchuser": "\"$1\" යන නමැති පරිශීලකයෙකු නොමැත.\nපරිශීලක නාමයන්හි මහාප්‍රාණ ආදිය සැලකේ (case sensitive).\nඔබගේ අක්ෂර-වින්‍යාසය පිරික්සා බැලීම හෝ, [[Special:CreateAccount|නව ගිණුමක් තැනීම]] හෝ සිදුකරන්න.",
+ "nosuchuser": "\"$1\" යන නමැති පරිශීලකයෙකු නොමැත.\nපරිශීලක නාමයන්හි මහාප්‍රාණ ආදිය සැලකේ (case-sensitive).\nඔබගේ අක්ෂර-වින්‍යාසය පිරික්සා බැලීම හෝ, [[Special:CreateAccount|නව ගිණුමක් තැනීම]] හෝ සිදුකරන්න.",
"nosuchusershort": "\"$1\" නමින් පරිශීලකයෙකු නොමැත.\nඅක්‍ෂර-වින්‍යාසය පිරික්සා බලන්න.",
"nouserspecified": "ඔබ විසින් පරිශීලක-නාමයක් සඳහන් කල යුතු වේ.",
"login-userblocked": "මෙම පරිශීලකයා වාරණය කොට ඇත. පිවිසීමට ඉඩ දෙනු නොලැබේ.",
diff --git a/languages/i18n/sr-ec.json b/languages/i18n/sr-ec.json
index cc7bf91f4282..f69fb3c8e46e 100644
--- a/languages/i18n/sr-ec.json
+++ b/languages/i18n/sr-ec.json
@@ -436,7 +436,7 @@
"cannotlogoutnow-title": "Одјава тренутно није могућа",
"cannotlogoutnow-text": "Одјава није могућа током употребе $1.",
"welcomeuser": "Добро дошли, $1.",
- "welcomecreation-msg": "Налог је отворен.\nМожете да промените [[Special:Preferences|подешавања]] на пројекту {{SITENAME}} ако желите.",
+ "welcomecreation-msg": "Налог је отворен.\nМожете да промените [[Special:Preferences|подешавања]] на {{SITENAME}} ако желите.",
"yourname": "Корисничко име:",
"userlogin-yourname": "Корисничко име",
"userlogin-yourname-ph": "Унесите корисничко име",
@@ -1119,6 +1119,7 @@
"prefs-searchmisc": "Опште",
"searchprefs": "Претрага подешавања",
"searchprefs-noresults": "Нема резултата",
+ "searchprefs-results": "$1 {{PLURAL:$1|резултат|резултата}}",
"saveprefs": "Сачувај",
"restoreprefs": "Врати све на подразумевано",
"prefs-editing": "Уређивање",
@@ -2391,9 +2392,9 @@
"enotif_reset": "Означи све странице као посећене",
"enotif_impersonal_salutation": "{{SITENAME}} корисник",
"enotif_subject_deleted": "Страницу $1 на {{SITENAME}} {{GENDER:$2|обрисао је|обрисала је|обрисао је}} $2",
- "enotif_subject_created": "$2 је {{GENDER:$2|направио|направила|направио/-ла}} страницу $1 на пројекту {{SITENAME}}",
- "enotif_subject_moved": "$2 је {{GENDER:$2|преместио|преместила|преместио/-ла}} страницу $1 на пројекту {{SITENAME}}",
- "enotif_subject_restored": "$2 је {{GENDER:$2|вратио|вратила}} страницу $1 на пројекту {{SITENAME}}",
+ "enotif_subject_created": "$2 је {{GENDER:$2|направио|направила}} страницу $1 на {{SITENAME}}",
+ "enotif_subject_moved": "$2 је {{GENDER:$2|преместио|преместила}} страницу $1 на {{SITENAME}}",
+ "enotif_subject_restored": "$2 је {{GENDER:$2|вратио|вратила}} страницу $1 на {{SITENAME}}",
"enotif_subject_changed": "Страницу $1 на {{SITENAME}} {{GENDER:$2|променио|променила}} је $2",
"enotif_body_intro_deleted": "Страницу $1 на {{SITENAME}} {{GENDER:$2|обрисао|обрисала}} је $2 дана $PAGEEDITDATE Погледајте $3.",
"enotif_body_intro_created": "Страницу $1 на пројекту {{SITENAME}} је {{GENDER:$2|направио корисник|направила корисница}} $2 на датум $PAGEEDITDATE Актуелна измена се налази на $3.",
diff --git a/languages/i18n/tr.json b/languages/i18n/tr.json
index 60ecfa906b4e..74bdaf6fff79 100644
--- a/languages/i18n/tr.json
+++ b/languages/i18n/tr.json
@@ -1201,6 +1201,7 @@
"prefs-searchmisc": "Genel",
"searchprefs": "Tercihleri ara",
"searchprefs-noresults": "Sonuç yok",
+ "searchprefs-results": "$1 {{PLURAL:$1|sonuç}}",
"saveprefs": "Kaydet",
"restoreprefs": "Tüm varsayılan ayarları geri yükle",
"prefs-editing": "Sayfa yazma alanı",
@@ -1992,6 +1993,12 @@
"uploadstash-errclear": "Dosyaların silinmesi başarısız oldu.",
"uploadstash-refresh": "Dosya listelerini yenile",
"uploadstash-exception": "Yükleme ($1) deposunda saklanamadı: \"$2\".",
+ "uploadstash-nothumb": "Küçük görsel yok",
+ "uploadstash-header-date": "Tarih",
+ "uploadstash-header-filekey": "Anahtar",
+ "uploadstash-header-thumb": "Küçük görsel",
+ "uploadstash-header-dimensions": "Boyutlar",
+ "uploadstash-pager-submit": "Dosya listesini göster",
"uploadstash-bad-path": "Yol mevcut değil.",
"uploadstash-bad-path-invalid": "Yol geçerli değil.",
"uploadstash-bad-path-unknown-type": "Bilinmeyen tür \"$1\".",
diff --git a/languages/i18n/ur.json b/languages/i18n/ur.json
index d62ccbeb1fb1..6a596fc43e1f 100644
--- a/languages/i18n/ur.json
+++ b/languages/i18n/ur.json
@@ -271,7 +271,7 @@
"mainpage-description": "صفحۂ اول",
"policy-url": "Project:حکمت عملی",
"portal": "چوپال",
- "portal-url": "Project:کمیونیٹی پورٹل",
+ "portal-url": "Project:چوپال",
"privacy": "اخفائے راز کے اصول",
"privacypage": "Project:اصولِ اخفائے راز",
"badaccess": "نقص اجازت",
diff --git a/languages/i18n/war.json b/languages/i18n/war.json
index add2b8e37fc9..55ea3c2bcca2 100644
--- a/languages/i18n/war.json
+++ b/languages/i18n/war.json
@@ -452,7 +452,7 @@
"noname": "Waray ka nakahatag hin maupay nga agnay-hit-gumaramit.",
"loginsuccesstitle": "Nakalog-in",
"loginsuccess": "'''Ikaw in nakalog-in ha {{SITENAME}} komo \"$1\".'''",
- "nosuchuser": "Waray gumaramit an may-ada ngaran nga \"$1\".\nIt mga agnay-hi-gumaramit in case sensitive.\nPanginano-a it imo pagbaybay, o [[Special:CreateAccount|paghimo hin bag-o nga akawnt]].",
+ "nosuchuser": "Waray gumaramit an may-ada ngaran nga \"$1\".\nIt mga agnay-hi-gumaramit in case-sensitive.\nPanginano-a it imo pagbaybay, o [[Special:CreateAccount|paghimo hin bag-o nga akawnt]].",
"nosuchusershort": "Waray nagamit it may ngaran nga \"$1\".\nKitaa kun amo it im pagbaybay.",
"nouserspecified": "Dapat nim magbutang hin agnay hit gumaramit.",
"login-userblocked": "Ini nga gumaramit in ginpugngan. Diri gintutugutan an pagsakob.",
diff --git a/languages/i18n/zh-hans.json b/languages/i18n/zh-hans.json
index 1b55726c9b07..ce7f778fff3e 100644
--- a/languages/i18n/zh-hans.json
+++ b/languages/i18n/zh-hans.json
@@ -107,6 +107,7 @@
"Nervie",
"NigelSoft",
"O",
+ "Olvcpr423",
"Onecountry",
"Phenolla",
"PhiLiP",
@@ -913,11 +914,11 @@
"editingsection": "编辑“$1”(章节)",
"editingcomment": "编辑“$1”(新章节)",
"editconflict": "编辑冲突:$1",
- "explainconflict": "其他用户在您开始编辑后更改了该页面。\n上面的文本区含有该页面当前的文字。\n下面的文本区显示您的更改。\n您必须把您的更改合并至现有文本。\n<strong>只有</strong>当您点击“$1”后,上面文本区中的文本才会被发布。",
+ "explainconflict": "其他用户在您开始编辑后更改了该页面。下方第一个文本区含有该页面当前的文本,第二个文本区显示您的更改。您需要把自己的更改合并至现有文本。在您点击“$1”后,<strong>只会</strong>发布第一个文本区中的文本。",
"yourtext": "您的文本",
"storedversion": "已保存的版本",
"editingold": "<strong>警告:您正在编辑的是本页面的旧版本。</strong>如果您发布该更改,该版本后的所有更改都会丢失。",
- "unicode-support-fail": "看起来您的浏览器不支持Unicode。需要Unicode才能编辑页面,所以您的编辑无法发布。",
+ "unicode-support-fail": "您的浏览器似乎不支持Unicode。需要Unicode才能编辑页面,所以您的编辑无法发布。",
"yourdiff": "差异",
"copyrightwarning": "请注意您对{{SITENAME}}的所有贡献都被认为是在$2下发布,请查看在$1的细节。如果您不希望您的文字被任意修改和再散布,请不要提交。<br />\n您同时也要向我们保证您所提交的内容是您自己所作,或得自一个不受版权保护或相似自由的来源。\n<strong>未经许可,请勿提交受版权保护的作品!</strong>",
"copyrightwarning2": "请注意,您对{{SITENAME}}的所有贡献都可能被其他贡献者编辑,修改或删除。如果您不希望您的文字被任意修改和再散布,请不要提交。<br />\n您同时也要向我们保证您所提交的内容是您自己所作,或得自一个不受版权保护或相似自由的来源(参阅$1的细节)。\n<strong>未经许可,请勿提交受版权保护的作品!</strong>",
@@ -1278,6 +1279,7 @@
"prefs-searchmisc": "通用",
"searchprefs": "在参数设置中搜索",
"searchprefs-noresults": "没有结果",
+ "searchprefs-results": "$1{{PLURAL:$1|个结果}}",
"saveprefs": "保存",
"restoreprefs": "还原所有默认设置",
"prefs-editing": "编辑",
diff --git a/languages/i18n/zh-hant.json b/languages/i18n/zh-hant.json
index 0ae9861837fa..758df4a0deb5 100644
--- a/languages/i18n/zh-hant.json
+++ b/languages/i18n/zh-hant.json
@@ -1221,6 +1221,7 @@
"prefs-searchmisc": "一般",
"searchprefs": "在偏好設定中搜尋",
"searchprefs-noresults": "沒有結果",
+ "searchprefs-results": "$1{{PLURAL:$1|個結果}}",
"saveprefs": "儲存",
"restoreprefs": "還原所有預設設定",
"prefs-editing": "編輯",
diff --git a/languages/messages/MessagesCs.php b/languages/messages/MessagesCs.php
index 70fc44e3d6fd..e11d1f80f372 100644
--- a/languages/messages/MessagesCs.php
+++ b/languages/messages/MessagesCs.php
@@ -28,14 +28,14 @@ $namespaceNames = [
];
$namespaceAliases = [
- 'Uživatel_diskuse' => NS_USER_TALK, # old literal translation backward compatibility
- 'Uživatelka_diskuse' => NS_USER_TALK, # female complement to old literal translation style
- '$1_diskuse' => NS_PROJECT_TALK, # old literal translation backward compatibility
- 'Soubor_diskuse' => NS_FILE_TALK, # old literal translation backward compatibility
+ 'Uživatel_diskuse' => NS_USER_TALK, # old literal translation backward compatibility
+ 'Uživatelka_diskuse' => NS_USER_TALK, # female complement to old literal translation style
+ '$1_diskuse' => NS_PROJECT_TALK, # old literal translation backward compatibility
+ 'Soubor_diskuse' => NS_FILE_TALK, # old literal translation backward compatibility
'MediaWiki_diskuse' => NS_MEDIAWIKI_TALK, # old literal translation backward compatibility
- 'Šablona_diskuse' => NS_TEMPLATE_TALK, # old literal translation backward compatibility
- 'Nápověda_diskuse' => NS_HELP_TALK, # old literal translation backward compatibility
- 'Kategorie_diskuse' => NS_CATEGORY_TALK, # old literal translation backward compatibility
+ 'Šablona_diskuse' => NS_TEMPLATE_TALK, # old literal translation backward compatibility
+ 'Nápověda_diskuse' => NS_HELP_TALK, # old literal translation backward compatibility
+ 'Kategorie_diskuse' => NS_CATEGORY_TALK, # old literal translation backward compatibility
];
$namespaceGenderAliases = [
diff --git a/maintenance/benchmarks/benchmarkParse.php b/maintenance/benchmarks/benchmarkParse.php
index 50a03c2e0688..04ab6eb173df 100644
--- a/maintenance/benchmarks/benchmarkParse.php
+++ b/maintenance/benchmarks/benchmarkParse.php
@@ -24,6 +24,7 @@
require_once __DIR__ . '/../Maintenance.php';
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Revision\SlotRecord;
diff --git a/maintenance/getLagTimes.php b/maintenance/getLagTimes.php
index 5e31c98417dd..c1d7466cf9f9 100644
--- a/maintenance/getLagTimes.php
+++ b/maintenance/getLagTimes.php
@@ -61,8 +61,12 @@ class GetLagTimes extends Maintenance {
$ip = gethostbyname( $host );
}
- $starLen = min( intval( $lag ), 40 );
- $stars = str_repeat( '*', $starLen );
+ if ( $lag === false ) {
+ $stars = 'replication stopped or errored';
+ } else {
+ $starLen = min( intval( $lag ), 40 );
+ $stars = str_repeat( '*', $starLen );
+ }
$this->output( sprintf( "%10s %20s %3d %s\n", $ip, $host, $lag, $stars ) );
if ( $this->hasOption( 'report' ) ) {
diff --git a/maintenance/updateCollation.php b/maintenance/updateCollation.php
index 3dfa27e43af5..1dfcd510e1bf 100644
--- a/maintenance/updateCollation.php
+++ b/maintenance/updateCollation.php
@@ -156,9 +156,7 @@ TEXT
if ( $this->hasOption( 'previous-collation' ) ) {
$collationConds['cl_collation'] = $this->getOption( 'previous-collation' );
} else {
- $collationConds = [
- 0 => $this->dbr->expr( 'cl_collation', '!=', $this->collationName )
- ];
+ $collationConds[] = $this->dbr->expr( 'cl_collation', '!=', $this->collationName );
}
}
$maxPageId = (int)$this->dbr->newSelectQueryBuilder()
@@ -222,10 +220,8 @@ TEXT
/**
* Update a set of rows in the categorylinks table
- *
- * @param IResultWrapper $res The rows to update
*/
- private function updateBatch( $res ) {
+ private function updateBatch( IResultWrapper $res ) {
if ( !$this->dryRun ) {
$this->beginTransaction( $this->dbw, __METHOD__ );
}
@@ -234,12 +230,12 @@ TEXT
if ( !$row->cl_collation ) {
# This is an old-style row, so the sortkey needs to be
# converted.
- if ( $row->cl_sortkey == $title->getText()
- || $row->cl_sortkey == $title->getPrefixedText()
+ if ( $row->cl_sortkey === $title->getText()
+ || $row->cl_sortkey === $title->getPrefixedText()
) {
$prefix = '';
} else {
- # Custom sortkey, use it as a prefix
+ # Custom sortkey, so use it as a prefix
$prefix = $row->cl_sortkey;
}
} else {
@@ -281,10 +277,8 @@ TEXT
/**
* Copy a set of rows to the target table
- *
- * @param IResultWrapper $res
*/
- private function copyBatch( $res ) {
+ private function copyBatch( IResultWrapper $res ) {
$sortKeyInputs = [];
foreach ( $res as $row ) {
$title = Title::newFromRow( $row );
@@ -327,10 +321,8 @@ TEXT
/**
* Update the verbose statistics
- *
- * @param string $key
*/
- private function updateSortKeySizeHistogram( $key ) {
+ private function updateSortKeySizeHistogram( string $key ) {
if ( !$this->verboseStats ) {
return;
}
@@ -349,7 +341,7 @@ TEXT
return;
}
$maxLength = max( array_keys( $this->sizeHistogram ) );
- if ( $maxLength == 0 ) {
+ if ( $maxLength === 0 ) {
return;
}
$numBins = 20;
@@ -374,7 +366,7 @@ TEXT
break;
}
}
- if ( $coarseIndex == $numBins - 1 ) {
+ if ( $coarseIndex === ( $numBins - 1 ) ) {
$coarseHistogram[$coarseIndex] += $val;
}
$raw .= $val;
@@ -383,16 +375,19 @@ TEXT
$this->output( "Sort key size histogram\nRaw data: $raw\n\n" );
$maxBinVal = max( $coarseHistogram );
- $scale = 60 / $maxBinVal;
+ $scale = (int)( 60 / $maxBinVal );
$prevBoundary = 0;
for ( $coarseIndex = 0; $coarseIndex < $numBins; $coarseIndex++ ) {
$val = $coarseHistogram[$coarseIndex] ?? 0;
// @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset False positive
$boundary = $coarseBoundaries[$coarseIndex];
- $this->output( sprintf( "%-10s %-10d |%s\n",
- $prevBoundary . '-' . ( $boundary - 1 ) . ': ',
- $val,
- str_repeat( '*', $scale * $val ) ) );
+ $this->output(
+ sprintf( "%-10s %-10d |%s\n",
+ $prevBoundary . '-' . ( $boundary - 1 ) . ': ',
+ $val,
+ str_repeat( '*', $scale * $val )
+ )
+ );
$prevBoundary = $boundary;
}
}
diff --git a/maintenance/userOptions.php b/maintenance/userOptions.php
index 660bd085f70f..e4cd537289a2 100644
--- a/maintenance/userOptions.php
+++ b/maintenance/userOptions.php
@@ -289,7 +289,7 @@ WARN
if ( !$dryRun ) {
$delete = $dbw->newDeleteQueryBuilder()
->deleteFrom( 'user_properties' )
- ->where( [ 'up_property' => $option, 'up_user' => $userIds ] );
+ ->where( [ 'up_property' => $option, 'up_user' => $userIds ] );
if ( $this->hasOption( 'old' ) ) {
$delete->andWhere( [ 'up_value' => $old ] );
}
diff --git a/resources/Resources.php b/resources/Resources.php
index b5251d3f3d04..fe9562e1f712 100644
--- a/resources/Resources.php
+++ b/resources/Resources.php
@@ -2271,7 +2271,9 @@ return [
'prefs-sections-navigation-hint',
'prefs-signature-highlight-error',
'prefs-back-title',
+ 'searchprefs',
'searchprefs-noresults',
+ 'searchprefs-results',
],
'dependencies' => [
'mediawiki.language',
diff --git a/resources/lib/codex/codex-search.cjs b/resources/lib/codex/codex-search.cjs
deleted file mode 100644
index 2009ebe9c9f4..000000000000
--- a/resources/lib/codex/codex-search.cjs
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";var pe=Object.defineProperty,me=Object.defineProperties;var fe=Object.getOwnPropertyDescriptors;var Q=Object.getOwnPropertySymbols;var oe=Object.prototype.hasOwnProperty,le=Object.prototype.propertyIsEnumerable;var ne=(e,n,o)=>n in e?pe(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,ae=(e,n)=>{for(var o in n||(n={}))oe.call(n,o)&&ne(e,o,n[o]);if(Q)for(var o of Q(n))le.call(n,o)&&ne(e,o,n[o]);return e},se=(e,n)=>me(e,fe(n));var z=(e,n)=>{var o={};for(var l in e)oe.call(e,l)&&n.indexOf(l)<0&&(o[l]=e[l]);if(e!=null&&Q)for(var l of Q(e))n.indexOf(l)<0&&le.call(e,l)&&(o[l]=e[l]);return o};var j=(e,n,o)=>new Promise((l,r)=>{var d=i=>{try{u(o.next(i))}catch(c){r(c)}},s=i=>{try{u(o.throw(i))}catch(c){r(c)}},u=i=>i.done?l(i.value):Promise.resolve(i.value).then(d,s);u((o=o.apply(e,n)).next())});Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("vue"),ge='<path d="M12.43 14.34A5 5 0 0110 15a5 5 0 113.95-2L17 16.09V3a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 001.45-.63z"/><circle cx="10" cy="10" r="3"/>',ve='<path d="M10 0a10 10 0 1010 10A10 10 0 0010 0m5.66 14.24-1.41 1.41L10 11.41l-4.24 4.25-1.42-1.42L8.59 10 4.34 5.76l1.42-1.42L10 8.59l4.24-4.24 1.41 1.41L11.41 10z"/>',ye='<path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/>',be='<path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/>',Ce=ge,ke=ve,Se=ye,we=be;function $e(e,n,o){if(typeof e=="string"||"path"in e)return e;if("shouldFlip"in e)return e.ltr;if("rtl"in e)return o==="rtl"?e.rtl:e.ltr;const l=n in e.langCodeMap?e.langCodeMap[n]:e.default;return typeof l=="string"||"path"in l?l:l.ltr}function Be(e,n){if(typeof e=="string")return!1;if("langCodeMap"in e){const o=n in e.langCodeMap?e.langCodeMap[n]:e.default;if(typeof o=="string")return!1;e=o}if("shouldFlipExceptions"in e&&Array.isArray(e.shouldFlipExceptions)){const o=e.shouldFlipExceptions.indexOf(n);return o===void 0||o===-1}return"shouldFlip"in e?e.shouldFlip:!1}function _e(e){const n=t.ref(null);return t.onMounted(()=>{const o=window.getComputedStyle(e.value).direction;n.value=o==="ltr"||o==="rtl"?o:null}),n}function xe(e){const n=t.ref("");return t.onMounted(()=>{let o=e.value;for(;o&&o.lang==="";)o=o.parentElement;n.value=o?o.lang:null}),n}function E(e){return n=>typeof n=="string"&&e.indexOf(n)!==-1}const W="cdx",Ie=["default","progressive","destructive"],Me=["normal","primary","quiet"],Ve=["medium","large"],Ee=["x-small","small","medium"],Ne=["text","search","number","email","month","password","tel","url","week","date","datetime-local","time"],ie=["default","warning","error","success"],Te=120,Le=500,K="cdx-menu-footer-item",Ke=Symbol("CdxFieldInputId"),De=Symbol("CdxFieldDescriptionId"),Re=Symbol("CdxFieldStatus"),Ae=Symbol("CdxDisabled"),Fe="".concat(W,"-no-invert"),ze=E(Ee),Oe=t.defineComponent({name:"CdxIcon",props:{icon:{type:[String,Object],required:!0},iconLabel:{type:String,default:""},lang:{type:String,default:null},dir:{type:String,default:null},size:{type:String,default:"medium",validator:ze}},setup(e){const n=t.ref(),o=_e(n),l=xe(n),r=t.computed(()=>{var m;return(m=e.dir)!=null?m:o.value}),d=t.computed(()=>{var m;return(m=e.lang)!=null?m:l.value}),s=t.computed(()=>({"cdx-icon--flipped":r.value==="rtl"&&d.value!==null&&Be(e.icon,d.value),["cdx-icon--".concat(e.size)]:!0})),u=t.computed(()=>{var m,f;return $e(e.icon,(m=d.value)!=null?m:"",(f=r.value)!=null?f:"ltr")}),i=t.computed(()=>typeof u.value=="string"?u.value:""),c=t.computed(()=>typeof u.value!="string"?u.value.path:"");return{rootElement:n,rootClasses:s,iconSvg:i,iconPath:c}}});const I=(e,n)=>{const o=e.__vccOpts||e;for(const[l,r]of n)o[l]=r;return o},qe=["aria-hidden"],He={key:0},Ue=["innerHTML"],Pe=["d"];function Qe(e,n,o,l,r,d){return t.openBlock(),t.createElementBlock("span",{ref:"rootElement",class:t.normalizeClass(["cdx-icon",e.rootClasses])},[(t.openBlock(),t.createElementBlock("svg",{xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",width:"20",height:"20",viewBox:"0 0 20 20","aria-hidden":e.iconLabel?void 0:!0},[e.iconLabel?(t.openBlock(),t.createElementBlock("title",He,t.toDisplayString(e.iconLabel),1)):t.createCommentVNode("",!0),e.iconSvg?(t.openBlock(),t.createElementBlock("g",{key:1,innerHTML:e.iconSvg},null,8,Ue)):(t.openBlock(),t.createElementBlock("path",{key:2,d:e.iconPath},null,8,Pe))],8,qe))],2)}const G=I(Oe,[["render",Qe]]),je=t.defineComponent({name:"CdxThumbnail",components:{CdxIcon:G},props:{thumbnail:{type:[Object,null],default:null},placeholderIcon:{type:[String,Object],default:Se}},setup:e=>{const n=t.ref(!1),o=t.ref({}),l=r=>{const d=r.replace(/([\\"\n])/g,"\\$1"),s=new Image;s.onload=()=>{o.value={backgroundImage:'url("'.concat(d,'")')},n.value=!0},s.onerror=()=>{n.value=!1},s.src=d};return t.onMounted(()=>{var r;(r=e.thumbnail)!=null&&r.url&&l(e.thumbnail.url)}),{thumbnailStyle:o,thumbnailLoaded:n,NoInvertClass:Fe}}});const We={class:"cdx-thumbnail"},Ge={key:0,class:"cdx-thumbnail__placeholder"};function Je(e,n,o,l,r,d){const s=t.resolveComponent("cdx-icon");return t.openBlock(),t.createElementBlock("span",We,[e.thumbnailLoaded?t.createCommentVNode("",!0):(t.openBlock(),t.createElementBlock("span",Ge,[t.createVNode(s,{icon:e.placeholderIcon,class:"cdx-thumbnail__placeholder__icon--vue"},null,8,["icon"])])),t.createVNode(t.Transition,{name:"cdx-thumbnail__image"},{default:t.withCtx(()=>[e.thumbnailLoaded?(t.openBlock(),t.createElementBlock("span",{key:0,style:t.normalizeStyle(e.thumbnailStyle),class:t.normalizeClass([e.NoInvertClass,"cdx-thumbnail__image"])},null,6)):t.createCommentVNode("",!0)]),_:1})])}const Xe=I(je,[["render",Je]]);function Ye(e){return e.replace(/([\\{}()|.?*+\-^$[\]])/g,"\\$1")}const Ze="[̀-ͯ҃-҉֑-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣ৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣૺ-૿ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఄా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഀ-ഃ഻഼ാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪾ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳷-᳹᷀-᷹᷻-᷿⃐-⃰⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯-꙲ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꣿꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯]";function et(e,n){if(!e)return[n,"",""];const o=Ye(e),l=new RegExp(o+Ze+"*","i").exec(n);if(!l||l.index===void 0)return[n,"",""];const r=l.index,d=r+l[0].length,s=n.slice(r,d),u=n.slice(0,r),i=n.slice(d,n.length);return[u,s,i]}const tt=t.defineComponent({name:"CdxSearchResultTitle",props:{title:{type:String,required:!0},searchQuery:{type:String,default:""}},setup:e=>({titleChunks:t.computed(()=>et(e.searchQuery,String(e.title)))})});const nt={class:"cdx-search-result-title"},ot={class:"cdx-search-result-title__match"};function lt(e,n,o,l,r,d){return t.openBlock(),t.createElementBlock("span",nt,[t.createElementVNode("bdi",null,[t.createTextVNode(t.toDisplayString(e.titleChunks[0]),1),t.createElementVNode("span",ot,t.toDisplayString(e.titleChunks[1]),1),t.createTextVNode(t.toDisplayString(e.titleChunks[2]),1)])])}const at=I(tt,[["render",lt]]),st=t.defineComponent({name:"CdxMenuItem",components:{CdxIcon:G,CdxThumbnail:Xe,CdxSearchResultTitle:at},props:{id:{type:String,required:!0},value:{type:[String,Number],required:!0},disabled:{type:Boolean,default:!1},selected:{type:Boolean,default:!1},active:{type:Boolean,default:!1},highlighted:{type:Boolean,default:!1},label:{type:String,default:""},match:{type:String,default:""},supportingText:{type:String,default:""},url:{type:String,default:""},icon:{type:[String,Object],default:""},showThumbnail:{type:Boolean,default:!1},thumbnail:{type:[Object,null],default:null},description:{type:[String,null],default:""},searchQuery:{type:String,default:""},boldLabel:{type:Boolean,default:!1},hideDescriptionOverflow:{type:Boolean,default:!1},language:{type:Object,default:()=>({})}},emits:["change"],setup:(e,{emit:n})=>{const o=()=>{e.highlighted||n("change","highlighted",!0)},l=()=>{n("change","highlighted",!1)},r=m=>{m.button===0&&n("change","active",!0)},d=()=>{n("change","selected",!0)},s=t.computed(()=>e.searchQuery.length>0),u=t.computed(()=>({"cdx-menu-item--selected":e.selected,"cdx-menu-item--active":e.active&&e.highlighted,"cdx-menu-item--highlighted":e.highlighted,"cdx-menu-item--enabled":!e.disabled,"cdx-menu-item--disabled":e.disabled,"cdx-menu-item--highlight-query":s.value,"cdx-menu-item--bold-label":e.boldLabel,"cdx-menu-item--has-description":!!e.description,"cdx-menu-item--hide-description-overflow":e.hideDescriptionOverflow})),i=t.computed(()=>e.url?"a":"span"),c=t.computed(()=>e.label||String(e.value));return{onMouseMove:o,onMouseLeave:l,onMouseDown:r,onClick:d,highlightQuery:s,rootClasses:u,contentTag:i,title:c}}});const it=["id","aria-disabled","aria-selected"],rt={class:"cdx-menu-item__text"},ut=["lang"],dt=["lang"],ct=["lang"],ht=["lang"];function pt(e,n,o,l,r,d){const s=t.resolveComponent("cdx-thumbnail"),u=t.resolveComponent("cdx-icon"),i=t.resolveComponent("cdx-search-result-title");return t.openBlock(),t.createElementBlock("li",{id:e.id,role:"option",class:t.normalizeClass(["cdx-menu-item",e.rootClasses]),"aria-disabled":e.disabled,"aria-selected":e.selected,onMousemove:n[0]||(n[0]=(...c)=>e.onMouseMove&&e.onMouseMove(...c)),onMouseleave:n[1]||(n[1]=(...c)=>e.onMouseLeave&&e.onMouseLeave(...c)),onMousedown:n[2]||(n[2]=t.withModifiers((...c)=>e.onMouseDown&&e.onMouseDown(...c),["prevent"])),onClick:n[3]||(n[3]=(...c)=>e.onClick&&e.onClick(...c))},[t.renderSlot(e.$slots,"default",{},()=>[(t.openBlock(),t.createBlock(t.resolveDynamicComponent(e.contentTag),{href:e.url?e.url:void 0,class:"cdx-menu-item__content"},{default:t.withCtx(()=>{var c,m,f,g,w,S;return[e.showThumbnail?(t.openBlock(),t.createBlock(s,{key:0,thumbnail:e.thumbnail,class:"cdx-menu-item__thumbnail"},null,8,["thumbnail"])):e.icon?(t.openBlock(),t.createBlock(u,{key:1,icon:e.icon,class:"cdx-menu-item__icon"},null,8,["icon"])):t.createCommentVNode("",!0),t.createElementVNode("span",rt,[e.highlightQuery?(t.openBlock(),t.createBlock(i,{key:0,title:e.title,"search-query":e.searchQuery,lang:(c=e.language)==null?void 0:c.label},null,8,["title","search-query","lang"])):(t.openBlock(),t.createElementBlock("span",{key:1,class:"cdx-menu-item__text__label",lang:(m=e.language)==null?void 0:m.label},[t.createElementVNode("bdi",null,t.toDisplayString(e.title),1)],8,ut)),e.match?(t.openBlock(),t.createElementBlock(t.Fragment,{key:2},[t.createTextVNode(t.toDisplayString(" ")+" "),e.highlightQuery?(t.openBlock(),t.createBlock(i,{key:0,title:e.match,"search-query":e.searchQuery,lang:(f=e.language)==null?void 0:f.match},null,8,["title","search-query","lang"])):(t.openBlock(),t.createElementBlock("span",{key:1,class:"cdx-menu-item__text__match",lang:(g=e.language)==null?void 0:g.match},[t.createElementVNode("bdi",null,t.toDisplayString(e.match),1)],8,dt))],64)):t.createCommentVNode("",!0),e.supportingText?(t.openBlock(),t.createElementBlock(t.Fragment,{key:3},[t.createTextVNode(t.toDisplayString(" ")+" "),t.createElementVNode("span",{class:"cdx-menu-item__text__supporting-text",lang:(w=e.language)==null?void 0:w.supportingText},[t.createElementVNode("bdi",null,t.toDisplayString(e.supportingText),1)],8,ct)],64)):t.createCommentVNode("",!0),e.description?(t.openBlock(),t.createElementBlock("span",{key:4,class:"cdx-menu-item__text__description",lang:(S=e.language)==null?void 0:S.description},[t.createElementVNode("bdi",null,t.toDisplayString(e.description),1)],8,ht)):t.createCommentVNode("",!0)])]}),_:1},8,["href"]))])],42,it)}const mt=I(st,[["render",pt]]);function re(e,n){if(e()){t.warn(n);return}const o=t.watch(e,l=>{l&&(t.warn(n),o())})}const ft=t.defineComponent({name:"CdxProgressBar",props:{inline:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1}},setup(e,{attrs:n}){re(()=>!e.inline&&!n["aria-label"]&&!n["aria-hidden"],"CdxProgressBar: Progress bars require one of the following attribute, aria-label or aria-hidden. See documentation on https://doc.wikimedia.org/codex/latest/components/demos/progressbar.html");const o=t.computed(()=>({"cdx-progress-bar--block":!e.inline,"cdx-progress-bar--inline":e.inline,"cdx-progress-bar--enabled":!e.disabled,"cdx-progress-bar--disabled":e.disabled})),l=t.computed(()=>e.inline?"true":void 0);return{rootClasses:o,computedAriaHidden:l}}});const gt=["aria-hidden","aria-disabled"],vt=t.createElementVNode("div",{class:"cdx-progress-bar__bar"},null,-1),yt=[vt];function bt(e,n,o,l,r,d){return t.openBlock(),t.createElementBlock("div",{class:t.normalizeClass(["cdx-progress-bar",e.rootClasses]),role:"progressbar","aria-hidden":e.computedAriaHidden,"aria-disabled":e.disabled},yt,10,gt)}const Ct=I(ft,[["render",bt]]);let te=0;function ue(e){var l;const n=t.getCurrentInstance(),o=(l=n==null?void 0:n.props.id)!=null?l:n==null?void 0:n.attrs.id;return e?"".concat(W,"-").concat(e,"-").concat(te++):o?"".concat(W,"-").concat(o,"-").concat(te++):"".concat(W,"-").concat(te++)}function kt(e,n){const o=t.ref(!1);let l=!1;if(typeof window!="object"||!("IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype))return o;const r=new window.IntersectionObserver(d=>{const s=d[0];s&&(o.value=s.isIntersecting)},n);return t.onMounted(()=>{l=!0,e.value&&r.observe(e.value)}),t.onUnmounted(()=>{l=!1,r.disconnect()}),t.watch(e,d=>{l&&(r.disconnect(),o.value=!1,d&&r.observe(d))}),o}function J(e,n=t.computed(()=>({}))){const o=t.computed(()=>{const d=z(n.value,[]);return e.class&&e.class.split(" ").forEach(u=>{d[u]=!0}),d}),l=t.computed(()=>{if("style"in e)return e.style}),r=t.computed(()=>{const i=e,{class:d,style:s}=i;return z(i,["class","style"])});return{rootClasses:o,rootStyle:l,otherAttrs:r}}const St=t.defineComponent({name:"CdxMenu",components:{CdxMenuItem:mt,CdxProgressBar:Ct},inheritAttrs:!1,props:{menuItems:{type:Array,required:!0},footer:{type:Object,default:null},selected:{type:[String,Number,null],required:!0},expanded:{type:Boolean,required:!0},showPending:{type:Boolean,default:!1},visibleItemLimit:{type:Number,default:null},showThumbnail:{type:Boolean,default:!1},boldLabel:{type:Boolean,default:!1},hideDescriptionOverflow:{type:Boolean,default:!1},searchQuery:{type:String,default:""},showNoResultsSlot:{type:Boolean,default:null}},emits:["update:selected","update:expanded","menu-item-click","menu-item-keyboard-navigation","load-more"],expose:["isExpanded","clearActive","getHighlightedMenuItem","getHighlightedViaKeyboard","delegateKeyNavigation"],setup(e,{emit:n,slots:o,attrs:l}){const r=t.computed(()=>(e.footer&&e.menuItems?[...e.menuItems,e.footer]:e.menuItems).map(h=>se(ae({},h),{id:ue("menu-item")}))),d=t.computed(()=>o["no-results"]?e.showNoResultsSlot!==null?e.showNoResultsSlot:r.value.length===0:!1),s=t.ref(null),u=t.ref(!1),i=t.ref(null),c="additions removals";let m="",f=null;function g(){m="",f!==null&&(clearTimeout(f),f=null)}function w(){f!==null&&clearTimeout(f),f=setTimeout(g,1500)}function S(){var a;return(a=r.value.find(h=>h.value===e.selected))!=null?a:null}function $(a,h){var C;if(!(h&&h.disabled))switch(a){case"selected":n("update:selected",(C=h==null?void 0:h.value)!=null?C:null),n("update:expanded",!1),i.value=null;break;case"highlighted":s.value=h!=null?h:null,u.value=!1;break;case"highlightedViaKeyboard":s.value=h!=null?h:null,u.value=!0;break;case"active":i.value=h!=null?h:null;break}}const b=t.computed(()=>{if(s.value!==null)return r.value.findIndex(a=>a.value===s.value.value)});function D(a){a&&($("highlightedViaKeyboard",a),n("menu-item-keyboard-navigation",a))}function R(a){var k;const h=L=>{for(let _=L-1;_>=0;_--)if(!r.value[_].disabled)return r.value[_]};a=a!=null?a:r.value.length;const C=(k=h(a))!=null?k:h(r.value.length);D(C)}function A(a){var k;const h=L=>r.value.find((_,P)=>!_.disabled&&P>L);a=a!=null?a:-1;const C=(k=h(a))!=null?k:h(-1);D(C)}function O(a){if(a.key==="Clear")return g(),!0;if(a.key==="Backspace")return m=m.slice(0,-1),w(),!0;if(a.key.length===1&&!a.metaKey&&!a.ctrlKey&&!a.altKey){if(e.expanded||n("update:expanded",!0),a.key===" "&&m.length<1)return!1;m+=a.key.toLowerCase();const h=m.length>1&&m.split("").every(_=>_===m[0]);let C=r.value,k=m;h&&b.value!==void 0&&(C=C.slice(b.value+1).concat(C.slice(0,b.value)),k=m[0]);const L=C.find(_=>{var P;return!_.disabled&&String((P=_.label)!=null?P:_.value).toLowerCase().startsWith(k)});return L&&($("highlightedViaKeyboard",L),V()),w(),!0}return!1}function q(a,{prevent:h=!0,characterNavigation:C=!1}={}){if(C){if(O(a))return a.preventDefault(),!0;g()}function k(){h&&(a.preventDefault(),a.stopPropagation())}switch(a.key){case"Enter":case" ":return k(),e.expanded?(s.value&&u.value&&n("update:selected",s.value.value),n("update:expanded",!1)):n("update:expanded",!0),!0;case"Tab":return e.expanded&&(s.value&&u.value&&n("update:selected",s.value.value),n("update:expanded",!1)),!0;case"ArrowUp":return k(),e.expanded?(s.value===null&&$("highlightedViaKeyboard",S()),R(b.value)):n("update:expanded",!0),V(),!0;case"ArrowDown":return k(),e.expanded?(s.value===null&&$("highlightedViaKeyboard",S()),A(b.value)):n("update:expanded",!0),V(),!0;case"Home":return k(),e.expanded?(s.value===null&&$("highlightedViaKeyboard",S()),A()):n("update:expanded",!0),V(),!0;case"End":return k(),e.expanded?(s.value===null&&$("highlightedViaKeyboard",S()),R()):n("update:expanded",!0),V(),!0;case"Escape":return k(),n("update:expanded",!1),!0;default:return!1}}function y(){$("active",null)}const B=[],x=t.ref(void 0),M=kt(x,{threshold:.8});t.watch(M,a=>{a&&n("load-more")});function H(a,h){if(a){B[h]=a.$el;const C=e.visibleItemLimit;if(!C||e.menuItems.length<C)return;const k=Math.min(C,Math.max(2,Math.floor(.2*e.menuItems.length)));h===e.menuItems.length-k&&(x.value=a.$el)}}function V(){if(!e.visibleItemLimit||e.visibleItemLimit>e.menuItems.length||b.value===void 0)return;const a=b.value>=0?b.value:0;B[a].scrollIntoView({behavior:"smooth",block:"nearest"})}const N=t.ref(null),T=t.ref(null);function F(){return j(this,null,function*(){yield t.nextTick(),U(),X(),yield t.nextTick(),V()})}function U(){if(e.footer){const a=B[B.length-1];T.value=a.scrollHeight}else T.value=null}function X(){if(!e.visibleItemLimit||B.length<=e.visibleItemLimit){N.value=null;return}const a=B[0].getBoundingClientRect().top,h=B[e.visibleItemLimit].getBoundingClientRect().top;N.value=h-a+2}t.onMounted(()=>{document.addEventListener("mouseup",y)}),t.onUnmounted(()=>{document.removeEventListener("mouseup",y)}),t.watch(t.toRef(e,"expanded"),a=>j(this,null,function*(){if(a){const h=S();h&&!s.value&&$("highlighted",h),yield F()}else $("highlighted",null)})),t.watch(t.toRef(e,"menuItems"),a=>j(this,null,function*(){a.length<B.length&&(B.length=a.length),e.expanded&&(yield F())}),{deep:!0});const Y=t.computed(()=>({"max-height":N.value?"".concat(N.value,"px"):void 0,"margin-bottom":T.value?"".concat(T.value,"px"):void 0})),Z=t.computed(()=>({"cdx-menu--has-footer":!!e.footer})),{rootClasses:ee,rootStyle:p,otherAttrs:v}=J(l,Z);return{listBoxStyle:Y,rootClasses:ee,rootStyle:p,otherAttrs:v,assignTemplateRef:H,computedMenuItems:r,computedShowNoResultsSlot:d,highlightedMenuItem:s,highlightedViaKeyboard:u,activeMenuItem:i,handleMenuItemChange:$,handleKeyNavigation:q,ariaRelevant:c}},methods:{isExpanded(){return this.expanded},getHighlightedMenuItem(){return this.expanded?this.highlightedMenuItem:null},getHighlightedViaKeyboard(){return this.highlightedViaKeyboard},clearActive(){this.handleMenuItemChange("active",null)},delegateKeyNavigation(e,{prevent:n=!0,characterNavigation:o=!1}={}){return this.handleKeyNavigation(e,{prevent:n,characterNavigation:o})}}});const wt=["aria-live","aria-relevant"],$t={key:0,class:"cdx-menu__pending cdx-menu-item"},Bt={key:1,class:"cdx-menu__no-results cdx-menu-item"};function _t(e,n,o,l,r,d){const s=t.resolveComponent("cdx-menu-item"),u=t.resolveComponent("cdx-progress-bar");return t.withDirectives((t.openBlock(),t.createElementBlock("div",{class:t.normalizeClass(["cdx-menu",e.rootClasses]),style:t.normalizeStyle(e.rootStyle)},[t.createElementVNode("ul",t.mergeProps({class:"cdx-menu__listbox",role:"listbox",style:e.listBoxStyle,"aria-live":e.showPending?"polite":void 0,"aria-relevant":e.showPending?e.ariaRelevant:void 0},e.otherAttrs),[e.showPending&&e.computedMenuItems.length===0&&e.$slots.pending?(t.openBlock(),t.createElementBlock("li",$t,[t.renderSlot(e.$slots,"pending")])):t.createCommentVNode("",!0),e.computedShowNoResultsSlot?(t.openBlock(),t.createElementBlock("li",Bt,[t.renderSlot(e.$slots,"no-results")])):t.createCommentVNode("",!0),(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(e.computedMenuItems,(i,c)=>{var m,f;return t.openBlock(),t.createBlock(s,t.mergeProps({key:i.value,ref_for:!0,ref:g=>e.assignTemplateRef(g,c)},i,{selected:i.value===e.selected,active:i.value===((m=e.activeMenuItem)==null?void 0:m.value),highlighted:i.value===((f=e.highlightedMenuItem)==null?void 0:f.value),"show-thumbnail":e.showThumbnail,"bold-label":e.boldLabel,"hide-description-overflow":e.hideDescriptionOverflow,"search-query":e.searchQuery,onChange:(g,w)=>e.handleMenuItemChange(g,w?i:null),onClick:g=>e.$emit("menu-item-click",i)}),{default:t.withCtx(()=>{var g,w;return[t.renderSlot(e.$slots,"default",{menuItem:i,active:i.value===((g=e.activeMenuItem)==null?void 0:g.value)&&i.value===((w=e.highlightedMenuItem)==null?void 0:w.value)})]}),_:2},1040,["selected","active","highlighted","show-thumbnail","bold-label","hide-description-overflow","search-query","onChange","onClick"])}),128)),e.showPending?(t.openBlock(),t.createBlock(u,{key:2,class:"cdx-menu__progress-bar",inline:!0})):t.createCommentVNode("",!0)],16,wt)],6)),[[t.vShow,e.expanded]])}const xt=I(St,[["render",_t]]);function de(e){const n=[];for(const o of e)typeof o.type=="string"||typeof o.type=="object"?n.push(o):o.type!==t.Comment&&(typeof o.children=="string"&&o.children.trim()!==""?n.push(o.children):Array.isArray(o.children)&&n.push(...de(o.children)));return n}function It(e,n){return typeof e.type=="object"&&"name"in e.type?n!==void 0?e.type.name===n:!0:!1}function Mt(e,n){return typeof e.type=="string"?n!==void 0?e.type===n.toLowerCase():!0:!1}function Vt(e){const n=typeof e=="function"?e():e;return n?de(n):[]}function Et(e,n,o){const l=t.computed(()=>{const r=Vt(e);if(r.length!==1)return!1;const d=r[0];return!!(typeof d=="object"&&(It(d,"CdxIcon")||Mt(d,"svg")))});return re(()=>l.value&&!n["aria-label"]&&!n["aria-hidden"],"".concat(o,": Icon-only buttons require one of the following attributes: aria-label or aria-hidden. See documentation at https://doc.wikimedia.org/codex/latest/components/demos/button.html#icon-only-button")),l}const Nt=E(Ie),Tt=E(Me),Lt=E(Ve),Kt=t.defineComponent({name:"CdxButton",props:{action:{type:String,default:"default",validator:Nt},weight:{type:String,default:"normal",validator:Tt},size:{type:String,default:"medium",validator:Lt}},emits:["click"],setup(e,{emit:n,slots:o,attrs:l}){const r=Et(o.default,l,"CdxButton"),d=t.ref(!1);return{rootClasses:t.computed(()=>({["cdx-button--action-".concat(e.action)]:!0,["cdx-button--weight-".concat(e.weight)]:!0,["cdx-button--size-".concat(e.size)]:!0,"cdx-button--framed":e.weight!=="quiet","cdx-button--icon-only":r.value,"cdx-button--is-active":d.value})),onClick:c=>{n("click",c)},setActive:c=>{d.value=c}}}});function Dt(e,n,o,l,r,d){return t.openBlock(),t.createElementBlock("button",{class:t.normalizeClass(["cdx-button",e.rootClasses]),onClick:n[0]||(n[0]=(...s)=>e.onClick&&e.onClick(...s)),onKeydown:n[1]||(n[1]=t.withKeys(s=>e.setActive(!0),["space","enter"])),onKeyup:n[2]||(n[2]=t.withKeys(s=>e.setActive(!1),["space","enter"]))},[t.renderSlot(e.$slots,"default")],34)}const Rt=I(Kt,[["render",Dt]]);function ce(e,n,o){return t.computed({get:()=>e.value,set:l=>n(o||"update:modelValue",l)})}function At(e){const n=t.inject(Ae,t.ref(!1));return t.computed(()=>n.value||e.value)}function he(e,n,o){const l=At(e),r=t.inject(Re,t.ref("default")),d=t.computed(()=>n!=null&&n.value&&n.value!=="default"?n.value:r.value),s=t.inject(Ke,void 0),u=t.computed(()=>{var i;return(i=s==null?void 0:s.value)!=null?i:o});return{computedDisabled:l,computedStatus:d,computedInputId:u}}const Ft=E(Ne),zt=E(ie),Ot=t.defineComponent({name:"CdxTextInput",components:{CdxIcon:G},inheritAttrs:!1,expose:["focus","blur"],props:{modelValue:{type:[String,Number],default:""},inputType:{type:String,default:"text",validator:Ft},status:{type:String,default:"default",validator:zt},disabled:{type:Boolean,default:!1},startIcon:{type:[String,Object],default:void 0},endIcon:{type:[String,Object],default:void 0},clearable:{type:Boolean,default:!1}},emits:["update:modelValue","keydown","input","change","focus","blur","clear"],setup(e,{emit:n,attrs:o}){const l=o.id,{computedDisabled:r,computedStatus:d,computedInputId:s}=he(t.toRef(e,"disabled"),t.toRef(e,"status"),l),u=t.inject(De,void 0),i=ce(t.toRef(e,"modelValue"),n),c=t.computed(()=>e.clearable&&!!i.value&&!r.value),m=t.computed(()=>({"cdx-text-input--has-start-icon":!!e.startIcon,"cdx-text-input--has-end-icon":!!e.endIcon,"cdx-text-input--clearable":c.value,["cdx-text-input--status-".concat(d.value)]:!0})),{rootClasses:f,rootStyle:g,otherAttrs:w}=J(o,m),S=t.computed(()=>{const x=w.value,{id:y}=x;return z(x,["id"])}),$=t.computed(()=>({"cdx-text-input__input--has-value":!!i.value}));return{computedInputId:s,descriptionId:u,wrappedModel:i,isClearable:c,rootClasses:f,rootStyle:g,otherAttrsMinusId:S,inputClasses:$,computedDisabled:r,onClear:y=>{i.value="",n("clear",y)},onInput:y=>{n("input",y)},onChange:y=>{n("change",y)},onKeydown:y=>{(y.key==="Home"||y.key==="End")&&!y.ctrlKey&&!y.metaKey||n("keydown",y)},onFocus:y=>{n("focus",y)},onBlur:y=>{n("blur",y)},cdxIconClear:ke}},methods:{focus(){this.$refs.input.focus()},blur(){this.$refs.input.blur()}}});const qt=["id","type","aria-describedby","disabled"];function Ht(e,n,o,l,r,d){const s=t.resolveComponent("cdx-icon");return t.openBlock(),t.createElementBlock("div",{class:t.normalizeClass(["cdx-text-input",e.rootClasses]),style:t.normalizeStyle(e.rootStyle)},[t.withDirectives(t.createElementVNode("input",t.mergeProps({id:e.computedInputId,ref:"input","onUpdate:modelValue":n[0]||(n[0]=u=>e.wrappedModel=u),class:["cdx-text-input__input",e.inputClasses]},e.otherAttrsMinusId,{type:e.inputType,"aria-describedby":e.descriptionId,disabled:e.computedDisabled,size:"1",onInput:n[1]||(n[1]=(...u)=>e.onInput&&e.onInput(...u)),onChange:n[2]||(n[2]=(...u)=>e.onChange&&e.onChange(...u)),onFocus:n[3]||(n[3]=(...u)=>e.onFocus&&e.onFocus(...u)),onBlur:n[4]||(n[4]=(...u)=>e.onBlur&&e.onBlur(...u)),onKeydown:n[5]||(n[5]=(...u)=>e.onKeydown&&e.onKeydown(...u))}),null,16,qt),[[t.vModelDynamic,e.wrappedModel]]),e.startIcon?(t.openBlock(),t.createBlock(s,{key:0,icon:e.startIcon,class:"cdx-text-input__icon-vue cdx-text-input__start-icon"},null,8,["icon"])):t.createCommentVNode("",!0),e.endIcon?(t.openBlock(),t.createBlock(s,{key:1,icon:e.endIcon,class:"cdx-text-input__icon-vue cdx-text-input__end-icon"},null,8,["icon"])):t.createCommentVNode("",!0),e.isClearable?(t.openBlock(),t.createBlock(s,{key:2,icon:e.cdxIconClear,class:"cdx-text-input__icon-vue cdx-text-input__clear-icon",onMousedown:n[6]||(n[6]=t.withModifiers(()=>{},["prevent"])),onClick:e.onClear},null,8,["icon","onClick"])):t.createCommentVNode("",!0)],6)}const Ut=I(Ot,[["render",Ht]]),Pt=E(ie),Qt=t.defineComponent({name:"CdxSearchInput",components:{CdxButton:Rt,CdxTextInput:Ut},inheritAttrs:!1,props:{modelValue:{type:[String,Number],default:""},buttonLabel:{type:String,default:""},disabled:{type:Boolean,default:!1},status:{type:String,default:"default",validator:Pt}},emits:["update:modelValue","submit-click","input","change","focus","blur"],setup(e,{emit:n,attrs:o}){const l=ce(t.toRef(e,"modelValue"),n),{computedDisabled:r}=he(t.toRef(e,"disabled")),d=t.computed(()=>({"cdx-search-input--has-end-button":!!e.buttonLabel})),{rootClasses:s,rootStyle:u,otherAttrs:i}=J(o,d);return{wrappedModel:l,computedDisabled:r,rootClasses:s,rootStyle:u,otherAttrs:i,handleSubmit:()=>{n("submit-click",l.value)},searchIcon:we}},methods:{focus(){this.$refs.textInput.focus()}}});const jt={class:"cdx-search-input__input-wrapper"};function Wt(e,n,o,l,r,d){const s=t.resolveComponent("cdx-text-input"),u=t.resolveComponent("cdx-button");return t.openBlock(),t.createElementBlock("div",{class:t.normalizeClass(["cdx-search-input",e.rootClasses]),style:t.normalizeStyle(e.rootStyle)},[t.createElementVNode("div",jt,[t.createVNode(s,t.mergeProps({ref:"textInput",modelValue:e.wrappedModel,"onUpdate:modelValue":n[0]||(n[0]=i=>e.wrappedModel=i),class:"cdx-search-input__text-input","input-type":"search","start-icon":e.searchIcon,disabled:e.computedDisabled,status:e.status},e.otherAttrs,{onKeydown:t.withKeys(e.handleSubmit,["enter"]),onInput:n[1]||(n[1]=i=>e.$emit("input",i)),onChange:n[2]||(n[2]=i=>e.$emit("change",i)),onFocus:n[3]||(n[3]=i=>e.$emit("focus",i)),onBlur:n[4]||(n[4]=i=>e.$emit("blur",i))}),null,16,["modelValue","start-icon","disabled","status","onKeydown"]),t.renderSlot(e.$slots,"default")]),e.buttonLabel?(t.openBlock(),t.createBlock(u,{key:0,class:"cdx-search-input__end-button",disabled:e.computedDisabled,onClick:e.handleSubmit},{default:t.withCtx(()=>[t.createTextVNode(t.toDisplayString(e.buttonLabel),1)]),_:1},8,["disabled","onClick"])):t.createCommentVNode("",!0)],6)}const Gt=I(Qt,[["render",Wt]]),Jt=t.defineComponent({name:"CdxTypeaheadSearch",components:{CdxIcon:G,CdxMenu:xt,CdxSearchInput:Gt},inheritAttrs:!1,props:{id:{type:String,required:!0},formAction:{type:String,required:!0},searchResultsLabel:{type:String,required:!0},searchResults:{type:Array,required:!0},buttonLabel:{type:String,default:""},initialInputValue:{type:String,default:""},searchFooterUrl:{type:String,default:""},debounceInterval:{type:Number,default:Te},highlightQuery:{type:Boolean,default:!1},showThumbnail:{type:Boolean,default:!1},autoExpandWidth:{type:Boolean,default:!1},visibleItemLimit:{type:Number,default:null}},emits:["input","search-result-click","submit","load-more"],setup(e,{attrs:n,emit:o,slots:l}){const r=t.ref(),d=t.ref(),s=ue("typeahead-search-menu"),u=t.ref(!1),i=t.ref(!1),c=t.ref(!1),m=t.ref(!1),f=t.ref(e.initialInputValue),g=t.ref(""),w=t.computed(()=>{var p,v;return(v=(p=d.value)==null?void 0:p.getHighlightedMenuItem())==null?void 0:v.id}),S=t.ref(null),$=t.computed(()=>({"cdx-typeahead-search__menu-message--has-thumbnail":e.showThumbnail})),b=t.computed(()=>e.searchResults.find(p=>p.value===S.value)),D=t.computed(()=>e.searchFooterUrl?{value:K,url:e.searchFooterUrl}:void 0),R=t.computed(()=>({"cdx-typeahead-search--show-thumbnail":e.showThumbnail,"cdx-typeahead-search--expanded":u.value,"cdx-typeahead-search--auto-expand-width":e.showThumbnail&&e.autoExpandWidth})),{rootClasses:A,rootStyle:O,otherAttrs:q}=J(n,R);function y(p){return p}const B=t.computed(()=>({visibleItemLimit:e.visibleItemLimit,showThumbnail:e.showThumbnail,boldLabel:!0,hideDescriptionOverflow:!0}));let x,M;function H(p,v=!1){b.value&&b.value.label!==p&&b.value.value!==p&&(S.value=null),M!==void 0&&(clearTimeout(M),M=void 0),p===""?u.value=!1:(i.value=!0,l["search-results-pending"]&&(M=setTimeout(()=>{m.value&&(u.value=!0),c.value=!0},Le))),x!==void 0&&(clearTimeout(x),x=void 0);const a=()=>{o("input",p)};v?a():x=setTimeout(()=>{a()},e.debounceInterval)}function V(p){var v;if(p===K){S.value=null,f.value=g.value;return}S.value=p,p!==null&&(f.value=b.value?(v=b.value.label)!=null?v:String(b.value.value):"")}function N(){m.value=!0,(g.value||c.value)&&(u.value=!0)}function T(){m.value=!1,u.value=!1}function F(p){const h=p,{id:v}=h,a=z(h,["id"]);if(a.value===K){o("search-result-click",{searchResult:null,index:e.searchResults.length,numberOfResults:e.searchResults.length});return}U(a)}function U(p){const v={searchResult:p,index:e.searchResults.findIndex(a=>a.value===p.value),numberOfResults:e.searchResults.length};o("search-result-click",v)}function X(p){var v;if(p.value===K){f.value=g.value;return}f.value=p.value?(v=p.label)!=null?v:String(p.value):""}function Y(p){var v;u.value=!1,(v=d.value)==null||v.clearActive(),F(p)}function Z(p){if(b.value)U(b.value),p.stopPropagation(),window.location.assign(b.value.url),p.preventDefault();else{const v={searchResult:null,index:-1,numberOfResults:e.searchResults.length};o("submit",v)}}function ee(p){if(!d.value||!g.value||p.key===" ")return;const v=d.value.getHighlightedMenuItem(),a=d.value.getHighlightedViaKeyboard();switch(p.key){case"Enter":v&&(v.value===K&&a?window.location.assign(e.searchFooterUrl):d.value.delegateKeyNavigation(p,{prevent:!1})),u.value=!1;break;case"Tab":u.value=!1;break;default:d.value.delegateKeyNavigation(p);break}}return t.onMounted(()=>{e.initialInputValue&&H(e.initialInputValue,!0)}),t.watch(t.toRef(e,"searchResults"),()=>{g.value=f.value.trim(),m.value&&i.value&&g.value.length>0&&(u.value=!0),M!==void 0&&(clearTimeout(M),M=void 0),i.value=!1,c.value=!1}),{form:r,menu:d,menuId:s,highlightedId:w,selection:S,menuMessageClass:$,footer:D,asSearchResult:y,inputValue:f,searchQuery:g,expanded:u,showPending:c,rootClasses:A,rootStyle:O,otherAttrs:q,menuConfig:B,onUpdateInputValue:H,onUpdateMenuSelection:V,onFocus:N,onBlur:T,onSearchResultClick:F,onSearchResultKeyboardNavigation:X,onSearchFooterClick:Y,onSubmit:Z,onKeydown:ee,MenuFooterValue:K,articleIcon:Ce}},methods:{focus(){this.$refs.searchInput.focus()}}});const Xt=["id","action"],Yt={class:"cdx-typeahead-search__menu-message__text"},Zt={class:"cdx-typeahead-search__menu-message__text"},en=["href","onClickCapture"],tn={class:"cdx-menu-item__text cdx-typeahead-search__search-footer__text"},nn={class:"cdx-typeahead-search__search-footer__query"};function on(e,n,o,l,r,d){const s=t.resolveComponent("cdx-icon"),u=t.resolveComponent("cdx-menu"),i=t.resolveComponent("cdx-search-input");return t.openBlock(),t.createElementBlock("div",{class:t.normalizeClass(["cdx-typeahead-search",e.rootClasses]),style:t.normalizeStyle(e.rootStyle)},[t.createElementVNode("form",{id:e.id,ref:"form",class:"cdx-typeahead-search__form",action:e.formAction,onSubmit:n[4]||(n[4]=(...c)=>e.onSubmit&&e.onSubmit(...c))},[t.createVNode(i,t.mergeProps({ref:"searchInput",modelValue:e.inputValue,"onUpdate:modelValue":n[3]||(n[3]=c=>e.inputValue=c),"button-label":e.buttonLabel},e.otherAttrs,{class:"cdx-typeahead-search__input",name:"search",role:"combobox",autocomplete:"off","aria-autocomplete":"list","aria-controls":e.menuId,"aria-expanded":e.expanded,"aria-activedescendant":e.highlightedId,"onUpdate:modelValue":e.onUpdateInputValue,onFocus:e.onFocus,onBlur:e.onBlur,onKeydown:e.onKeydown}),{default:t.withCtx(()=>[t.createVNode(u,t.mergeProps({id:e.menuId,ref:"menu",expanded:e.expanded,"onUpdate:expanded":n[0]||(n[0]=c=>e.expanded=c),class:"cdx-typeahead-search__menu","show-pending":e.showPending,selected:e.selection,"menu-items":e.searchResults,footer:e.footer,"search-query":e.highlightQuery?e.searchQuery:"","show-no-results-slot":e.searchQuery.length>0&&e.searchResults.length===0&&e.$slots["search-no-results-text"]&&e.$slots["search-no-results-text"]().length>0},e.menuConfig,{"aria-label":e.searchResultsLabel,"onUpdate:selected":e.onUpdateMenuSelection,onMenuItemClick:n[1]||(n[1]=c=>e.onSearchResultClick(e.asSearchResult(c))),onMenuItemKeyboardNavigation:e.onSearchResultKeyboardNavigation,onLoadMore:n[2]||(n[2]=c=>e.$emit("load-more"))}),{pending:t.withCtx(()=>[t.createElementVNode("div",{class:t.normalizeClass(["cdx-menu-item__content cdx-typeahead-search__menu-message",e.menuMessageClass])},[t.createElementVNode("span",Yt,[t.renderSlot(e.$slots,"search-results-pending")])],2)]),"no-results":t.withCtx(()=>[t.createElementVNode("div",{class:t.normalizeClass(["cdx-menu-item__content cdx-typeahead-search__menu-message",e.menuMessageClass])},[t.createElementVNode("span",Zt,[t.renderSlot(e.$slots,"search-no-results-text")])],2)]),default:t.withCtx(({menuItem:c,active:m})=>[c.value===e.MenuFooterValue?(t.openBlock(),t.createElementBlock("a",{key:0,class:t.normalizeClass(["cdx-menu-item__content cdx-typeahead-search__search-footer",{"cdx-typeahead-search__search-footer__active":m}]),href:e.asSearchResult(c).url,onClickCapture:t.withModifiers(f=>e.onSearchFooterClick(e.asSearchResult(c)),["stop"])},[t.createVNode(s,{class:"cdx-menu-item__thumbnail cdx-typeahead-search__search-footer__icon",icon:e.articleIcon},null,8,["icon"]),t.createElementVNode("span",tn,[t.renderSlot(e.$slots,"search-footer-text",{searchQuery:e.searchQuery},()=>[t.createElementVNode("strong",nn,t.toDisplayString(e.searchQuery),1)])])],42,en)):t.createCommentVNode("",!0)]),_:3},16,["id","expanded","show-pending","selected","menu-items","footer","search-query","show-no-results-slot","aria-label","onUpdate:selected","onMenuItemKeyboardNavigation"])]),_:3},16,["modelValue","button-label","aria-controls","aria-expanded","aria-activedescendant","onUpdate:modelValue","onFocus","onBlur","onKeydown"]),t.renderSlot(e.$slots,"default")],40,Xt)],6)}const ln=I(Jt,[["render",on]]);exports.CdxTypeaheadSearch=ln;
diff --git a/resources/lib/codex/codex-search.js b/resources/lib/codex/codex-search.js
deleted file mode 100644
index b1b7fb10bc8a..000000000000
--- a/resources/lib/codex/codex-search.js
+++ /dev/null
@@ -1,2126 +0,0 @@
-var Oe = Object.defineProperty, He = Object.defineProperties;
-var qe = Object.getOwnPropertyDescriptors;
-var ce = Object.getOwnPropertySymbols;
-var xe = Object.prototype.hasOwnProperty, ke = Object.prototype.propertyIsEnumerable;
-var Ie = (e, t, n) => t in e ? Oe(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n, Me = (e, t) => {
- for (var n in t || (t = {}))
- xe.call(t, n) && Ie(e, n, t[n]);
- if (ce)
- for (var n of ce(t))
- ke.call(t, n) && Ie(e, n, t[n]);
- return e;
-}, Te = (e, t) => He(e, qe(t));
-var ae = (e, t) => {
- var n = {};
- for (var a in e)
- xe.call(e, a) && t.indexOf(a) < 0 && (n[a] = e[a]);
- if (e != null && ce)
- for (var a of ce(e))
- t.indexOf(a) < 0 && ke.call(e, a) && (n[a] = e[a]);
- return n;
-};
-var he = (e, t, n) => new Promise((a, i) => {
- var r = (s) => {
- try {
- u(n.next(s));
- } catch (d) {
- i(d);
- }
- }, o = (s) => {
- try {
- u(n.throw(s));
- } catch (d) {
- i(d);
- }
- }, u = (s) => s.done ? a(s.value) : Promise.resolve(s.value).then(r, o);
- u((n = n.apply(e, t)).next());
-});
-import { ref as b, onMounted as Y, defineComponent as F, computed as h, openBlock as v, createElementBlock as m, normalizeClass as K, toDisplayString as L, createCommentVNode as k, resolveComponent as A, createVNode as G, Transition as Ue, withCtx as q, normalizeStyle as oe, createElementVNode as _, createTextVNode as le, withModifiers as we, renderSlot as E, createBlock as R, resolveDynamicComponent as ze, Fragment as Se, warn as Be, watch as J, getCurrentInstance as Qe, onUnmounted as Le, toRef as U, nextTick as Ve, withDirectives as Ke, mergeProps as X, renderList as Pe, vShow as je, Comment as We, withKeys as _e, inject as pe, vModelDynamic as Ge } from "vue";
-const Je = '<path d="M12.43 14.34A5 5 0 0110 15a5 5 0 113.95-2L17 16.09V3a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 001.45-.63z"/><circle cx="10" cy="10" r="3"/>', Xe = '<path d="M10 0a10 10 0 1010 10A10 10 0 0010 0m5.66 14.24-1.41 1.41L10 11.41l-4.24 4.25-1.42-1.42L8.59 10 4.34 5.76l1.42-1.42L10 8.59l4.24-4.24 1.41 1.41L11.41 10z"/>', Ye = '<path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/>', Ze = '<path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/>', et = Je, tt = Xe, nt = Ye, at = Ze;
-function lt(e, t, n) {
- if (typeof e == "string" || "path" in e)
- return e;
- if ("shouldFlip" in e)
- return e.ltr;
- if ("rtl" in e)
- return n === "rtl" ? e.rtl : e.ltr;
- const a = t in e.langCodeMap ? e.langCodeMap[t] : e.default;
- return typeof a == "string" || "path" in a ? a : a.ltr;
-}
-function ot(e, t) {
- if (typeof e == "string")
- return !1;
- if ("langCodeMap" in e) {
- const n = t in e.langCodeMap ? e.langCodeMap[t] : e.default;
- if (typeof n == "string")
- return !1;
- e = n;
- }
- if ("shouldFlipExceptions" in e && Array.isArray(e.shouldFlipExceptions)) {
- const n = e.shouldFlipExceptions.indexOf(t);
- return n === void 0 || n === -1;
- }
- return "shouldFlip" in e ? e.shouldFlip : !1;
-}
-function st(e) {
- const t = b(null);
- return Y(() => {
- const n = window.getComputedStyle(e.value).direction;
- t.value = n === "ltr" || n === "rtl" ? n : null;
- }), t;
-}
-function it(e) {
- const t = b("");
- return Y(() => {
- let n = e.value;
- for (; n && n.lang === ""; )
- n = n.parentElement;
- t.value = n ? n.lang : null;
- }), t;
-}
-function z(e) {
- return (t) => typeof t == "string" && e.indexOf(t) !== -1;
-}
-const fe = "cdx", ut = [
- "default",
- "progressive",
- "destructive"
-], rt = [
- "normal",
- "primary",
- "quiet"
-], dt = [
- "medium",
- "large"
-], ct = [
- "x-small",
- "small",
- "medium"
-], ht = [
- "text",
- "search",
- "number",
- "email",
- "month",
- "password",
- "tel",
- "url",
- "week",
- "date",
- "datetime-local",
- "time"
-], Ae = [
- "default",
- "warning",
- "error",
- "success"
-], ft = 120, pt = 500, W = "cdx-menu-footer-item", vt = Symbol("CdxFieldInputId"), gt = Symbol("CdxFieldDescriptionId"), mt = Symbol("CdxFieldStatus"), yt = Symbol("CdxDisabled"), bt = "".concat(fe, "-no-invert"), Ct = z(ct), $t = F({
- name: "CdxIcon",
- props: {
- /** The SVG path or an object containing that path plus other data. */
- icon: {
- type: [String, Object],
- required: !0
- },
- /**
- * Accessible label for the icon. If not included, the icon will be hidden from screen
- * readers via `aria-hidden="true"`. Browsers also display this label as a tooltip when the
- * user hovers over the icon. Note that this label is not rendered as visible text next
- * to the icon.
- */
- iconLabel: {
- type: String,
- default: ""
- },
- /**
- * Explicitly set the language code to use for the icon. See
- * https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/lang.
- * Defaults to the lang attribute of the nearest ancestor at mount time.
- */
- lang: {
- type: String,
- default: null
- },
- /**
- * Explicitly set the direction to use for the icon. See
- * https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dir.
- * Defaults to the computed direction at mount time.
- */
- dir: {
- type: String,
- default: null
- },
- /**
- * Specify icon size by choosing one of several pre-defined size
- * options. See the type documentation for supported size options.
- * The `medium` size is used by default if no size prop is provided.
- */
- size: {
- type: String,
- default: "medium",
- validator: Ct
- }
- },
- setup(e) {
- const t = b(), n = st(t), a = it(t), i = h(() => {
- var p;
- return (p = e.dir) != null ? p : n.value;
- }), r = h(() => {
- var p;
- return (p = e.lang) != null ? p : a.value;
- }), o = h(() => ({
- "cdx-icon--flipped": i.value === "rtl" && r.value !== null && ot(e.icon, r.value),
- ["cdx-icon--".concat(e.size)]: !0
- })), u = h(
- () => {
- var p, g;
- return lt(e.icon, (p = r.value) != null ? p : "", (g = i.value) != null ? g : "ltr");
- }
- ), s = h(() => typeof u.value == "string" ? u.value : ""), d = h(() => typeof u.value != "string" ? u.value.path : "");
- return {
- rootElement: t,
- rootClasses: o,
- iconSvg: s,
- iconPath: d
- };
- }
-});
-const N = (e, t) => {
- const n = e.__vccOpts || e;
- for (const [a, i] of t)
- n[a] = i;
- return n;
-}, St = ["aria-hidden"], _t = { key: 0 }, wt = ["innerHTML"], It = ["d"];
-function xt(e, t, n, a, i, r) {
- return v(), m("span", {
- ref: "rootElement",
- class: K(["cdx-icon", e.rootClasses])
- }, [
- (v(), m("svg", {
- xmlns: "http://www.w3.org/2000/svg",
- "xmlns:xlink": "http://www.w3.org/1999/xlink",
- width: "20",
- height: "20",
- viewBox: "0 0 20 20",
- "aria-hidden": e.iconLabel ? void 0 : !0
- }, [
- e.iconLabel ? (v(), m("title", _t, L(e.iconLabel), 1)) : k("", !0),
- e.iconSvg ? (v(), m("g", {
- key: 1,
- innerHTML: e.iconSvg
- }, null, 8, wt)) : (v(), m("path", {
- key: 2,
- d: e.iconPath
- }, null, 8, It))
- ], 8, St))
- ], 2);
-}
-const ve = /* @__PURE__ */ N($t, [["render", xt]]), kt = F({
- name: "CdxThumbnail",
- components: { CdxIcon: ve },
- props: {
- /**
- * Thumbnail data.
- */
- thumbnail: {
- type: [Object, null],
- default: null
- },
- /**
- * Thumbnail placeholder icon.
- */
- placeholderIcon: {
- type: [String, Object],
- default: nt
- }
- },
- setup: (e) => {
- const t = b(!1), n = b({}), a = (i) => {
- const r = i.replace(/([\\"\n])/g, "\\$1"), o = new Image();
- o.onload = () => {
- n.value = { backgroundImage: 'url("'.concat(r, '")') }, t.value = !0;
- }, o.onerror = () => {
- t.value = !1;
- }, o.src = r;
- };
- return Y(() => {
- var i;
- (i = e.thumbnail) != null && i.url && a(e.thumbnail.url);
- }), {
- thumbnailStyle: n,
- thumbnailLoaded: t,
- NoInvertClass: bt
- };
- }
-});
-const Mt = { class: "cdx-thumbnail" }, Tt = {
- key: 0,
- class: "cdx-thumbnail__placeholder"
-};
-function Bt(e, t, n, a, i, r) {
- const o = A("cdx-icon");
- return v(), m("span", Mt, [
- e.thumbnailLoaded ? k("", !0) : (v(), m("span", Tt, [
- G(o, {
- icon: e.placeholderIcon,
- class: "cdx-thumbnail__placeholder__icon--vue"
- }, null, 8, ["icon"])
- ])),
- G(Ue, { name: "cdx-thumbnail__image" }, {
- default: q(() => [
- e.thumbnailLoaded ? (v(), m("span", {
- key: 0,
- style: oe(e.thumbnailStyle),
- class: K([e.NoInvertClass, "cdx-thumbnail__image"])
- }, null, 6)) : k("", !0)
- ]),
- _: 1
- })
- ]);
-}
-const Vt = /* @__PURE__ */ N(kt, [["render", Bt]]);
-function Lt(e) {
- return e.replace(/([\\{}()|.?*+\-^$[\]])/g, "\\$1");
-}
-const Kt = "[̀-ͯ҃-҉֑-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣ৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣૺ-૿ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఄా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഀ-ഃ഻഼ാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪾ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳷-᳹᷀-᷹᷻-᷿⃐-⃰⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯-꙲ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꣿꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯]";
-function At(e, t) {
- if (!e)
- return [t, "", ""];
- const n = Lt(e), a = new RegExp(
- // Per https://www.regular-expressions.info/unicode.html, "any code point that is not a
- // combining mark can be followed by any number of combining marks." See also the
- // discussion in https://phabricator.wikimedia.org/T35242.
- n + Kt + "*",
- "i"
- ).exec(t);
- if (!a || a.index === void 0)
- return [t, "", ""];
- const i = a.index, r = i + a[0].length, o = t.slice(i, r), u = t.slice(0, i), s = t.slice(r, t.length);
- return [u, o, s];
-}
-const Rt = F({
- name: "CdxSearchResultTitle",
- props: {
- /**
- * Title text.
- */
- title: {
- type: String,
- required: !0
- },
- /**
- * The current search query.
- */
- searchQuery: {
- type: String,
- default: ""
- }
- },
- setup: (e) => ({
- titleChunks: h(() => At(e.searchQuery, String(e.title)))
- })
-});
-const Dt = { class: "cdx-search-result-title" }, Et = { class: "cdx-search-result-title__match" };
-function Ft(e, t, n, a, i, r) {
- return v(), m("span", Dt, [
- _("bdi", null, [
- le(L(e.titleChunks[0]), 1),
- _("span", Et, L(e.titleChunks[1]), 1),
- le(L(e.titleChunks[2]), 1)
- ])
- ]);
-}
-const Nt = /* @__PURE__ */ N(Rt, [["render", Ft]]), Ot = F({
- name: "CdxMenuItem",
- components: { CdxIcon: ve, CdxThumbnail: Vt, CdxSearchResultTitle: Nt },
- props: {
- /**
- * ID for HTML `id` attribute.
- */
- id: {
- type: String,
- required: !0
- },
- /**
- * The value provided to the parent menu component when this menu item is selected.
- */
- value: {
- type: [String, Number],
- required: !0
- },
- /**
- * Whether the menu item is disabled.
- */
- disabled: {
- type: Boolean,
- default: !1
- },
- /**
- * Whether this menu item is selected.
- */
- selected: {
- type: Boolean,
- default: !1
- },
- /**
- * Whether this menu item is being pressed.
- */
- active: {
- type: Boolean,
- default: !1
- },
- /**
- * Whether this menu item is visually highlighted due to hover or keyboard navigation.
- */
- highlighted: {
- type: Boolean,
- default: !1
- },
- /**
- * Label for the menu item. If this isn't provided, the value will be displayed instead.
- */
- label: {
- type: String,
- default: ""
- },
- /**
- * Text that matches current search query. Only used for search results and will be
- * displayed after the label.
- */
- match: {
- type: String,
- default: ""
- },
- /**
- * Text that supports the label. Supporting text will appear next to the label in a more
- * subtle color.
- */
- supportingText: {
- type: String,
- default: ""
- },
- /**
- * URL for the menu item. If provided, the content of the menu item will be wrapped in an
- * anchor tag.
- */
- url: {
- type: String,
- default: ""
- },
- /**
- * Icon for the menu item.
- */
- icon: {
- type: [String, Object],
- default: ""
- },
- /**
- * Whether a thumbnail (or a placeholder icon) should be displayed.
- */
- showThumbnail: {
- type: Boolean,
- default: !1
- },
- /**
- * Thumbnail for the menu item.
- */
- thumbnail: {
- type: [Object, null],
- default: null
- },
- /**
- * Description of the menu item.
- */
- description: {
- type: [String, null],
- default: ""
- },
- /**
- * The search query to be highlighted within the menu item's title.
- */
- searchQuery: {
- type: String,
- default: ""
- },
- /**
- * Whether to bold menu item labels.
- */
- boldLabel: {
- type: Boolean,
- default: !1
- },
- /**
- * Whether to hide description text overflow via an ellipsis.
- */
- hideDescriptionOverflow: {
- type: Boolean,
- default: !1
- },
- /**
- * Optional language codes for label, match, supporting text, and description.
- *
- * If included, that language code will be added as a `lang` attribute to the element
- * wrapping that text node.
- */
- language: {
- type: Object,
- default: () => ({})
- }
- },
- emits: [
- /**
- * Emitted when the menu item becomes selected, active or highlighted in response to
- * user interaction. Handled in the Menu component.
- *
- * @property {MenuState} menuState State to change
- * @property {boolean} setState Whether to set that state to this menu item
- */
- "change"
- ],
- setup: (e, { emit: t }) => {
- const n = () => {
- e.highlighted || t("change", "highlighted", !0);
- }, a = () => {
- t("change", "highlighted", !1);
- }, i = (p) => {
- p.button === 0 && t("change", "active", !0);
- }, r = () => {
- t("change", "selected", !0);
- }, o = h(() => e.searchQuery.length > 0), u = h(() => ({
- "cdx-menu-item--selected": e.selected,
- // Only show the active visual state when the menu item is both active and
- // highlighted. This means, on mousedown -> mouseleave, the menu item is still
- // technically tracked by the menu as active, but will not appear active to the
- // user. This also means in the case of mousedown -> mouseleave -> mouseenter, the
- // menu item will appear active again, and on click (releasing the mouse button),
- // the item will be selected.
- "cdx-menu-item--active": e.active && e.highlighted,
- "cdx-menu-item--highlighted": e.highlighted,
- "cdx-menu-item--enabled": !e.disabled,
- "cdx-menu-item--disabled": e.disabled,
- "cdx-menu-item--highlight-query": o.value,
- "cdx-menu-item--bold-label": e.boldLabel,
- "cdx-menu-item--has-description": !!e.description,
- "cdx-menu-item--hide-description-overflow": e.hideDescriptionOverflow
- })), s = h(() => e.url ? "a" : "span"), d = h(() => e.label || String(e.value));
- return {
- onMouseMove: n,
- onMouseLeave: a,
- onMouseDown: i,
- onClick: r,
- highlightQuery: o,
- rootClasses: u,
- contentTag: s,
- title: d
- };
- }
-});
-const Ht = ["id", "aria-disabled", "aria-selected"], qt = { class: "cdx-menu-item__text" }, Ut = ["lang"], zt = ["lang"], Qt = ["lang"], Pt = ["lang"];
-function jt(e, t, n, a, i, r) {
- const o = A("cdx-thumbnail"), u = A("cdx-icon"), s = A("cdx-search-result-title");
- return v(), m("li", {
- id: e.id,
- role: "option",
- class: K(["cdx-menu-item", e.rootClasses]),
- "aria-disabled": e.disabled,
- "aria-selected": e.selected,
- onMousemove: t[0] || (t[0] = (...d) => e.onMouseMove && e.onMouseMove(...d)),
- onMouseleave: t[1] || (t[1] = (...d) => e.onMouseLeave && e.onMouseLeave(...d)),
- onMousedown: t[2] || (t[2] = we((...d) => e.onMouseDown && e.onMouseDown(...d), ["prevent"])),
- onClick: t[3] || (t[3] = (...d) => e.onClick && e.onClick(...d))
- }, [
- E(e.$slots, "default", {}, () => [
- (v(), R(ze(e.contentTag), {
- href: e.url ? e.url : void 0,
- class: "cdx-menu-item__content"
- }, {
- default: q(() => {
- var d, p, g, y, M, x;
- return [
- e.showThumbnail ? (v(), R(o, {
- key: 0,
- thumbnail: e.thumbnail,
- class: "cdx-menu-item__thumbnail"
- }, null, 8, ["thumbnail"])) : e.icon ? (v(), R(u, {
- key: 1,
- icon: e.icon,
- class: "cdx-menu-item__icon"
- }, null, 8, ["icon"])) : k("", !0),
- _("span", qt, [
- e.highlightQuery ? (v(), R(s, {
- key: 0,
- title: e.title,
- "search-query": e.searchQuery,
- lang: (d = e.language) == null ? void 0 : d.label
- }, null, 8, ["title", "search-query", "lang"])) : (v(), m("span", {
- key: 1,
- class: "cdx-menu-item__text__label",
- lang: (p = e.language) == null ? void 0 : p.label
- }, [
- _("bdi", null, L(e.title), 1)
- ], 8, Ut)),
- e.match ? (v(), m(Se, { key: 2 }, [
- le(L(" ") + " "),
- e.highlightQuery ? (v(), R(s, {
- key: 0,
- title: e.match,
- "search-query": e.searchQuery,
- lang: (g = e.language) == null ? void 0 : g.match
- }, null, 8, ["title", "search-query", "lang"])) : (v(), m("span", {
- key: 1,
- class: "cdx-menu-item__text__match",
- lang: (y = e.language) == null ? void 0 : y.match
- }, [
- _("bdi", null, L(e.match), 1)
- ], 8, zt))
- ], 64)) : k("", !0),
- e.supportingText ? (v(), m(Se, { key: 3 }, [
- le(L(" ") + " "),
- _("span", {
- class: "cdx-menu-item__text__supporting-text",
- lang: (M = e.language) == null ? void 0 : M.supportingText
- }, [
- _("bdi", null, L(e.supportingText), 1)
- ], 8, Qt)
- ], 64)) : k("", !0),
- e.description ? (v(), m("span", {
- key: 4,
- class: "cdx-menu-item__text__description",
- lang: (x = e.language) == null ? void 0 : x.description
- }, [
- _("bdi", null, L(e.description), 1)
- ], 8, Pt)) : k("", !0)
- ])
- ];
- }),
- _: 1
- }, 8, ["href"]))
- ])
- ], 42, Ht);
-}
-const Wt = /* @__PURE__ */ N(Ot, [["render", jt]]);
-function Re(e, t) {
- if (e()) {
- Be(t);
- return;
- }
- const n = J(e, (a) => {
- a && (Be(t), n());
- });
-}
-const Gt = F({
- name: "CdxProgressBar",
- props: {
- /**
- * Whether this is the smaller, inline variant.
- */
- inline: {
- type: Boolean,
- default: !1
- },
- /**
- * Whether the progress bar is disabled.
- */
- disabled: {
- type: Boolean,
- default: !1
- }
- },
- setup(e, { attrs: t }) {
- Re(
- () => !e.inline && !t["aria-label"] && !t["aria-hidden"],
- "CdxProgressBar: Progress bars require one of the following attribute, aria-label or aria-hidden. See documentation on https://doc.wikimedia.org/codex/latest/components/demos/progressbar.html"
- );
- const n = h(() => ({
- "cdx-progress-bar--block": !e.inline,
- "cdx-progress-bar--inline": e.inline,
- "cdx-progress-bar--enabled": !e.disabled,
- "cdx-progress-bar--disabled": e.disabled
- })), a = h(() => e.inline ? "true" : void 0);
- return {
- rootClasses: n,
- computedAriaHidden: a
- };
- }
-});
-const Jt = ["aria-hidden", "aria-disabled"], Xt = /* @__PURE__ */ _("div", { class: "cdx-progress-bar__bar" }, null, -1), Yt = [
- Xt
-];
-function Zt(e, t, n, a, i, r) {
- return v(), m("div", {
- class: K(["cdx-progress-bar", e.rootClasses]),
- role: "progressbar",
- "aria-hidden": e.computedAriaHidden,
- "aria-disabled": e.disabled
- }, Yt, 10, Jt);
-}
-const en = /* @__PURE__ */ N(Gt, [["render", Zt]]);
-let $e = 0;
-function De(e) {
- var a;
- const t = Qe(), n = (a = t == null ? void 0 : t.props.id) != null ? a : t == null ? void 0 : t.attrs.id;
- return e ? "".concat(fe, "-").concat(e, "-").concat($e++) : n ? "".concat(fe, "-").concat(n, "-").concat($e++) : "".concat(fe, "-").concat($e++);
-}
-function tn(e, t) {
- const n = b(!1);
- let a = !1;
- if (typeof window != "object" || !("IntersectionObserver" in window && "IntersectionObserverEntry" in window && "intersectionRatio" in window.IntersectionObserverEntry.prototype))
- return n;
- const i = new window.IntersectionObserver(
- (r) => {
- const o = r[0];
- o && (n.value = o.isIntersecting);
- },
- t
- );
- return Y(() => {
- a = !0, e.value && i.observe(e.value);
- }), Le(() => {
- a = !1, i.disconnect();
- }), J(e, (r) => {
- a && (i.disconnect(), n.value = !1, r && i.observe(r));
- }), n;
-}
-function ge(e, t = h(() => ({}))) {
- const n = h(() => {
- const r = ae(t.value, []);
- return e.class && e.class.split(" ").forEach((u) => {
- r[u] = !0;
- }), r;
- }), a = h(() => {
- if ("style" in e)
- return e.style;
- }), i = h(() => {
- const s = e, { class: r, style: o } = s;
- return ae(s, ["class", "style"]);
- });
- return {
- rootClasses: n,
- rootStyle: a,
- otherAttrs: i
- };
-}
-const nn = F({
- name: "CdxMenu",
- components: {
- CdxMenuItem: Wt,
- CdxProgressBar: en
- },
- /**
- * Attributes, besides class and style, will be passed to the <ul> element.
- */
- inheritAttrs: !1,
- props: {
- /** Menu items. See the MenuItemData type. */
- menuItems: {
- type: Array,
- required: !0
- },
- /**
- * Interactive footer item.
- *
- * This is a special menu item which is pinned to the bottom of the menu. When scrolling is
- * enabled within the menu, the footer item will always be visible at the bottom of the
- * menu. When scrolling is not enabled, the footer item will simply appear as the last menu
- * item.
- *
- * The footer item is selectable, like other menu items.
- */
- footer: {
- type: Object,
- default: null
- },
- /**
- * Value of the selected menu item, or undefined if no item is selected.
- *
- * Must be bound with `v-model:selected`.
- *
- * The property should be initialized to `null` rather than using a falsy value.
- */
- selected: {
- type: [String, Number, null],
- required: !0
- },
- /**
- * Whether the menu is expanded. Must be bound with `v-model:expanded`.
- */
- expanded: {
- type: Boolean,
- required: !0
- },
- /**
- * Whether to display pending state indicators. Meant to indicate that new menu items are
- * being fetched or computed.
- *
- * When true, the menu will expand if not already expanded, and an inline progress bar will
- * display. If there are no menu items yet, a message can be displayed in the `pending`
- * slot, e.g. "Loading results".
- */
- showPending: {
- type: Boolean,
- default: !1
- },
- /**
- * Limit the number of menu items to display before scrolling.
- *
- * Setting this prop to anything falsy will show all menu items.
- *
- * By default, all menu items are shown.
- */
- visibleItemLimit: {
- type: Number,
- default: null
- },
- /**
- * Whether menu item thumbnails (or a placeholder icon) should be displayed.
- */
- showThumbnail: {
- type: Boolean,
- default: !1
- },
- /**
- * Whether to bold menu item labels.
- */
- boldLabel: {
- type: Boolean,
- default: !1
- },
- /**
- * Whether to hide description text overflow via an ellipsis.
- */
- hideDescriptionOverflow: {
- type: Boolean,
- default: !1
- },
- /**
- * The search query to be highlighted within the menu items' titles.
- */
- searchQuery: {
- type: String,
- default: ""
- },
- /**
- * Whether to show the `no-results` slot content.
- *
- * The Menu component automatically shows this slot when there is content in the
- * `no-results` slot and there are zero menu items. However, some components may need to
- * customize this behavior, e.g. to show the slot even when there is at least one menu item.
- * This prop can be used to override the default Menu behavior.
- *
- * Possible values:
- * `null` (default): the `no-results` slot will display only if there are zero menu items.
- * `true`: the `no-results` slot will display, regardless of number of menu items.
- * `false`: the `no-results` slot will not display, regardless of number of menu items.
- */
- showNoResultsSlot: {
- type: Boolean,
- default: null
- }
- },
- emits: [
- // Don't remove the spaces in the "string | number | null" type below; removing these
- // spaces causes the documentation to render the type as "union" instead.
- /**
- * When the selected menu item changes.
- *
- * @property {string | number | null} selectedValue The `.value` property of the
- * selected menu item, or null if no item is selected.
- */
- "update:selected",
- /**
- * When the menu opens or closes.
- *
- * @property {boolean} newValue The new expanded state (true for open, false for closed)
- */
- "update:expanded",
- /**
- * When a menu item is clicked.
- *
- * Typically, components with menus will respond to the selected value change, but
- * occasionally, a component might want to react specifically when a menu item is clicked.
- *
- * @property {MenuItemDataWithId} menuItem The menu item that was clicked
- */
- "menu-item-click",
- /**
- * When a menu item is highlighted via keyboard navigation.
- *
- * @property {MenuItemDataWithId} highlightedMenuItem The menu item
- * was highlighted
- */
- "menu-item-keyboard-navigation",
- /**
- * When the user scrolls towards the bottom of the menu.
- *
- * If it is possible to add or load more menu items, then now would be a good moment
- * so that the user can experience infinite scrolling.
- */
- "load-more"
- ],
- expose: [
- "isExpanded",
- "clearActive",
- "getHighlightedMenuItem",
- "getHighlightedViaKeyboard",
- "delegateKeyNavigation"
- ],
- setup(e, { emit: t, slots: n, attrs: a }) {
- const i = h(() => (e.footer && e.menuItems ? [...e.menuItems, e.footer] : e.menuItems).map((c) => Te(Me({}, c), {
- id: De("menu-item")
- }))), r = h(() => n["no-results"] ? e.showNoResultsSlot !== null ? e.showNoResultsSlot : i.value.length === 0 : !1), o = b(null), u = b(!1), s = b(null), d = "additions removals";
- let p = "", g = null;
- function y() {
- p = "", g !== null && (clearTimeout(g), g = null);
- }
- function M() {
- g !== null && clearTimeout(g), g = setTimeout(y, 1500);
- }
- function x() {
- var l;
- return (l = i.value.find(
- (c) => c.value === e.selected
- )) != null ? l : null;
- }
- function T(l, c) {
- var w;
- if (!(c && c.disabled))
- switch (l) {
- case "selected":
- t("update:selected", (w = c == null ? void 0 : c.value) != null ? w : null), t("update:expanded", !1), s.value = null;
- break;
- case "highlighted":
- o.value = c != null ? c : null, u.value = !1;
- break;
- case "highlightedViaKeyboard":
- o.value = c != null ? c : null, u.value = !0;
- break;
- case "active":
- s.value = c != null ? c : null;
- break;
- }
- }
- const S = h(() => {
- if (o.value !== null)
- return i.value.findIndex(
- (l) => (
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- l.value === o.value.value
- )
- );
- });
- function Z(l) {
- l && (T("highlightedViaKeyboard", l), t("menu-item-keyboard-navigation", l));
- }
- function ee(l) {
- var I;
- const c = (j) => {
- for (let V = j - 1; V >= 0; V--)
- if (!i.value[V].disabled)
- return i.value[V];
- };
- l = l != null ? l : i.value.length;
- const w = (I = c(l)) != null ? I : c(i.value.length);
- Z(w);
- }
- function te(l) {
- var I;
- const c = (j) => i.value.find((V, de) => !V.disabled && de > j);
- l = l != null ? l : -1;
- const w = (I = c(l)) != null ? I : c(-1);
- Z(w);
- }
- function se(l) {
- if (l.key === "Clear")
- return y(), !0;
- if (l.key === "Backspace")
- return p = p.slice(0, -1), M(), !0;
- if (l.key.length === 1 && !l.metaKey && !l.ctrlKey && !l.altKey) {
- if (e.expanded || t("update:expanded", !0), l.key === " " && p.length < 1)
- return !1;
- p += l.key.toLowerCase();
- const c = p.length > 1 && p.split("").every((V) => V === p[0]);
- let w = i.value, I = p;
- c && S.value !== void 0 && (w = w.slice(S.value + 1).concat(w.slice(0, S.value)), I = p[0]);
- const j = w.find(
- (V) => {
- var de;
- return !V.disabled && String((de = V.label) != null ? de : V.value).toLowerCase().startsWith(I);
- }
- );
- return j && (T("highlightedViaKeyboard", j), H()), M(), !0;
- }
- return !1;
- }
- function ie(l, { prevent: c = !0, characterNavigation: w = !1 } = {}) {
- if (w) {
- if (se(l))
- return l.preventDefault(), !0;
- y();
- }
- function I() {
- c && (l.preventDefault(), l.stopPropagation());
- }
- switch (l.key) {
- case "Enter":
- case " ":
- return I(), e.expanded ? (o.value && u.value && t("update:selected", o.value.value), t("update:expanded", !1)) : t("update:expanded", !0), !0;
- case "Tab":
- return e.expanded && (o.value && u.value && t("update:selected", o.value.value), t("update:expanded", !1)), !0;
- case "ArrowUp":
- return I(), e.expanded ? (o.value === null && T("highlightedViaKeyboard", x()), ee(S.value)) : t("update:expanded", !0), H(), !0;
- case "ArrowDown":
- return I(), e.expanded ? (o.value === null && T("highlightedViaKeyboard", x()), te(S.value)) : t("update:expanded", !0), H(), !0;
- case "Home":
- return I(), e.expanded ? (o.value === null && T("highlightedViaKeyboard", x()), te()) : t("update:expanded", !0), H(), !0;
- case "End":
- return I(), e.expanded ? (o.value === null && T("highlightedViaKeyboard", x()), ee()) : t("update:expanded", !0), H(), !0;
- case "Escape":
- return I(), t("update:expanded", !1), !0;
- default:
- return !1;
- }
- }
- function $() {
- T("active", null);
- }
- const B = [], D = b(void 0), O = tn(
- D,
- { threshold: 0.8 }
- );
- J(O, (l) => {
- l && t("load-more");
- });
- function ue(l, c) {
- if (l) {
- B[c] = l.$el;
- const w = e.visibleItemLimit;
- if (!w || e.menuItems.length < w)
- return;
- const I = Math.min(
- w,
- Math.max(2, Math.floor(0.2 * e.menuItems.length))
- );
- c === e.menuItems.length - I && (D.value = l.$el);
- }
- }
- function H() {
- if (!e.visibleItemLimit || e.visibleItemLimit > e.menuItems.length || S.value === void 0)
- return;
- const l = S.value >= 0 ? S.value : 0;
- B[l].scrollIntoView({
- behavior: "smooth",
- block: "nearest"
- });
- }
- const Q = b(null), P = b(null);
- function ne() {
- return he(this, null, function* () {
- yield Ve(), re(), me(), yield Ve(), H();
- });
- }
- function re() {
- if (e.footer) {
- const l = B[B.length - 1];
- P.value = l.scrollHeight;
- } else
- P.value = null;
- }
- function me() {
- if (!e.visibleItemLimit || B.length <= e.visibleItemLimit) {
- Q.value = null;
- return;
- }
- const l = B[0].getBoundingClientRect().top, c = B[e.visibleItemLimit].getBoundingClientRect().top;
- Q.value = c - l + 2;
- }
- Y(() => {
- document.addEventListener("mouseup", $);
- }), Le(() => {
- document.removeEventListener("mouseup", $);
- }), J(U(e, "expanded"), (l) => he(this, null, function* () {
- if (l) {
- const c = x();
- c && !o.value && T("highlighted", c), yield ne();
- } else
- T("highlighted", null);
- })), J(U(e, "menuItems"), (l) => he(this, null, function* () {
- l.length < B.length && (B.length = l.length), e.expanded && (yield ne());
- }), { deep: !0 });
- const ye = h(() => ({
- "max-height": Q.value ? "".concat(Q.value, "px") : void 0,
- "margin-bottom": P.value ? "".concat(P.value, "px") : void 0
- })), be = h(() => ({
- "cdx-menu--has-footer": !!e.footer
- })), {
- rootClasses: Ce,
- rootStyle: f,
- otherAttrs: C
- } = ge(a, be);
- return {
- listBoxStyle: ye,
- rootClasses: Ce,
- rootStyle: f,
- otherAttrs: C,
- assignTemplateRef: ue,
- computedMenuItems: i,
- computedShowNoResultsSlot: r,
- highlightedMenuItem: o,
- highlightedViaKeyboard: u,
- activeMenuItem: s,
- handleMenuItemChange: T,
- handleKeyNavigation: ie,
- ariaRelevant: d
- };
- },
- // Public methods
- // These must be in the methods block, not in the setup function, otherwise their documentation
- // won't be picked up by vue-docgen
- methods: {
- /**
- * Returns whether the menu is expanded.
- *
- * @return {boolean}
- */
- isExpanded() {
- return this.expanded;
- },
- /**
- * Get the highlighted menu item, if any.
- *
- * The parent component should set `aria-activedescendant` to the `.id` property of the
- * object returned by this method. If this method returns null, `aria-activedescendant`
- * should not be set.
- *
- * @public
- * @return {MenuItemDataWithId|null} The highlighted menu item,
- * or null if no item is highlighted or if the menu is closed.
- */
- getHighlightedMenuItem() {
- return this.expanded ? this.highlightedMenuItem : null;
- },
- /**
- * Get whether the last highlighted item was highlighted via the keyboard.
- *
- * @public
- * @return {boolean} Whether the last highlighted menu item was highlighted via keyboard.
- */
- getHighlightedViaKeyboard() {
- return this.highlightedViaKeyboard;
- },
- /**
- * Ensure no menu item is active. This unsets the active item if there is one.
- *
- * @public
- */
- clearActive() {
- this.handleMenuItemChange("active", null);
- },
- /**
- * Handles all necessary keyboard navigation.
- *
- * The parent component should listen for keydown events on its focusable element,
- * and pass those events to this method. Events for arrow keys, tab and enter are handled
- * by this method. If a different key was pressed, this method will return false to indicate
- * that it didn't handle the event.
- *
- * @public
- * @param event {KeyboardEvent} Keydown event object
- * @param options
- * @param options.prevent {boolean} If false, do not call e.preventDefault() or
- * e.stopPropagation()
- * @param options.characterNavigation {boolean}
- * @return Whether the event was handled
- */
- delegateKeyNavigation(e, { prevent: t = !0, characterNavigation: n = !1 } = {}) {
- return this.handleKeyNavigation(e, { prevent: t, characterNavigation: n });
- }
- }
-});
-const an = ["aria-live", "aria-relevant"], ln = {
- key: 0,
- class: "cdx-menu__pending cdx-menu-item"
-}, on = {
- key: 1,
- class: "cdx-menu__no-results cdx-menu-item"
-};
-function sn(e, t, n, a, i, r) {
- const o = A("cdx-menu-item"), u = A("cdx-progress-bar");
- return Ke((v(), m("div", {
- class: K(["cdx-menu", e.rootClasses]),
- style: oe(e.rootStyle)
- }, [
- _("ul", X({
- class: "cdx-menu__listbox",
- role: "listbox",
- style: e.listBoxStyle,
- "aria-live": e.showPending ? "polite" : void 0,
- "aria-relevant": e.showPending ? e.ariaRelevant : void 0
- }, e.otherAttrs), [
- e.showPending && e.computedMenuItems.length === 0 && e.$slots.pending ? (v(), m("li", ln, [
- E(e.$slots, "pending")
- ])) : k("", !0),
- e.computedShowNoResultsSlot ? (v(), m("li", on, [
- E(e.$slots, "no-results")
- ])) : k("", !0),
- (v(!0), m(Se, null, Pe(e.computedMenuItems, (s, d) => {
- var p, g;
- return v(), R(o, X({
- key: s.value,
- ref_for: !0,
- ref: (y) => e.assignTemplateRef(y, d)
- }, s, {
- selected: s.value === e.selected,
- active: s.value === ((p = e.activeMenuItem) == null ? void 0 : p.value),
- highlighted: s.value === ((g = e.highlightedMenuItem) == null ? void 0 : g.value),
- "show-thumbnail": e.showThumbnail,
- "bold-label": e.boldLabel,
- "hide-description-overflow": e.hideDescriptionOverflow,
- "search-query": e.searchQuery,
- onChange: (y, M) => e.handleMenuItemChange(y, M ? s : null),
- onClick: (y) => e.$emit("menu-item-click", s)
- }), {
- default: q(() => {
- var y, M;
- return [
- E(e.$slots, "default", {
- menuItem: s,
- active: s.value === ((y = e.activeMenuItem) == null ? void 0 : y.value) && s.value === ((M = e.highlightedMenuItem) == null ? void 0 : M.value)
- })
- ];
- }),
- _: 2
- }, 1040, ["selected", "active", "highlighted", "show-thumbnail", "bold-label", "hide-description-overflow", "search-query", "onChange", "onClick"]);
- }), 128)),
- e.showPending ? (v(), R(u, {
- key: 2,
- class: "cdx-menu__progress-bar",
- inline: !0
- })) : k("", !0)
- ], 16, an)
- ], 6)), [
- [je, e.expanded]
- ]);
-}
-const un = /* @__PURE__ */ N(nn, [["render", sn]]);
-function Ee(e) {
- const t = [];
- for (const n of e)
- // HTML tag
- typeof n.type == "string" || // Component
- typeof n.type == "object" ? t.push(n) : n.type !== We && (typeof n.children == "string" && n.children.trim() !== "" ? t.push(n.children) : Array.isArray(n.children) && t.push(...Ee(n.children)));
- return t;
-}
-function rn(e, t) {
- return typeof e.type == "object" && "name" in e.type ? t !== void 0 ? e.type.name === t : !0 : !1;
-}
-function dn(e, t) {
- return typeof e.type == "string" ? t !== void 0 ? e.type === t.toLowerCase() : !0 : !1;
-}
-function cn(e) {
- const t = typeof e == "function" ? e() : e;
- return t ? Ee(t) : [];
-}
-function hn(e, t, n) {
- const a = h(() => {
- const i = cn(e);
- if (i.length !== 1)
- return !1;
- const r = i[0];
- return !!(typeof r == "object" && (rn(r, "CdxIcon") || dn(r, "svg")));
- });
- return Re(
- () => a.value && !t["aria-label"] && !t["aria-hidden"],
- "".concat(n, ": Icon-only buttons require one of the following attributes: aria-label or aria-hidden. See documentation at https://doc.wikimedia.org/codex/latest/components/demos/button.html#icon-only-button")
- ), a;
-}
-const fn = z(ut), pn = z(rt), vn = z(dt), gn = F({
- name: "CdxButton",
- props: {
- /**
- * The kind of action that will be taken on click.
- *
- * @values 'default', 'progressive', 'destructive'
- */
- action: {
- type: String,
- default: "default",
- validator: fn
- },
- /**
- * Visual prominence of the button.
- *
- * @values 'normal', 'primary', 'quiet'
- */
- weight: {
- type: String,
- default: "normal",
- validator: pn
- },
- /**
- * Button size.
- *
- * Most buttons should use the default medium size. In rare cases the large size should
- * be used, for example to make icon-only buttons larger on touchscreens.
- *
- * @values 'medium', 'large'
- */
- size: {
- type: String,
- default: "medium",
- validator: vn
- }
- },
- emits: ["click"],
- setup(e, { emit: t, slots: n, attrs: a }) {
- const i = hn(n.default, a, "CdxButton"), r = b(!1);
- return {
- rootClasses: h(() => ({
- ["cdx-button--action-".concat(e.action)]: !0,
- ["cdx-button--weight-".concat(e.weight)]: !0,
- ["cdx-button--size-".concat(e.size)]: !0,
- "cdx-button--framed": e.weight !== "quiet",
- "cdx-button--icon-only": i.value,
- "cdx-button--is-active": r.value
- })),
- onClick: (d) => {
- t("click", d);
- },
- setActive: (d) => {
- r.value = d;
- }
- };
- }
-});
-function mn(e, t, n, a, i, r) {
- return v(), m("button", {
- class: K(["cdx-button", e.rootClasses]),
- onClick: t[0] || (t[0] = (...o) => e.onClick && e.onClick(...o)),
- onKeydown: t[1] || (t[1] = _e((o) => e.setActive(!0), ["space", "enter"])),
- onKeyup: t[2] || (t[2] = _e((o) => e.setActive(!1), ["space", "enter"]))
- }, [
- E(e.$slots, "default")
- ], 34);
-}
-const yn = /* @__PURE__ */ N(gn, [["render", mn]]);
-function Fe(e, t, n) {
- return h({
- get: () => e.value,
- set: (a) => (
- // If eventName is undefined, then 'update:modelValue' must be a valid EventName,
- // but TypeScript's type analysis isn't clever enough to realize that
- t(n || "update:modelValue", a)
- )
- });
-}
-function bn(e) {
- const t = pe(yt, b(!1));
- return h(() => t.value || e.value);
-}
-function Ne(e, t, n) {
- const a = bn(e), i = pe(mt, b("default")), r = h(() => t != null && t.value && t.value !== "default" ? t.value : i.value), o = pe(vt, void 0), u = h(() => {
- var s;
- return (s = o == null ? void 0 : o.value) != null ? s : n;
- });
- return {
- computedDisabled: a,
- computedStatus: r,
- computedInputId: u
- };
-}
-const Cn = z(ht), $n = z(Ae), Sn = F({
- name: "CdxTextInput",
- components: { CdxIcon: ve },
- /**
- * We want the input to inherit attributes, not the root element.
- */
- inheritAttrs: !1,
- expose: [
- "focus",
- "blur"
- ],
- props: {
- /**
- * Current value of the input.
- *
- * Provided by `v-model` binding in the parent component.
- */
- modelValue: {
- type: [String, Number],
- default: ""
- },
- /**
- * `type` attribute of the input.
- *
- * @values 'text', 'search', 'number', 'email', 'password', 'tel', 'url',
- * 'week', 'month', 'date', 'datetime-local', 'time'
- */
- inputType: {
- type: String,
- default: "text",
- validator: Cn
- },
- /**
- * `status` attribute of the input.
- */
- status: {
- type: String,
- default: "default",
- validator: $n
- },
- /**
- * Whether the input is disabled.
- */
- disabled: {
- type: Boolean,
- default: !1
- },
- /**
- * An icon at the start of the input element. Similar to a `::before` pseudo-element.
- */
- startIcon: {
- type: [String, Object],
- default: void 0
- },
- /**
- * An icon at the end of the input element. Similar to an `::after` pseudo-element.
- */
- endIcon: {
- type: [String, Object],
- default: void 0
- },
- /**
- * Add a clear button at the end of the input element.
- *
- * When the clear button is pressed, the input's value is set to an empty string.
- * The clear button is displayed when input text is present.
- */
- clearable: {
- type: Boolean,
- default: !1
- }
- },
- emits: [
- /**
- * When the input value changes
- *
- * @property {string | number} modelValue The new model value
- */
- "update:modelValue",
- /**
- * When the user presses a key.
- *
- * This event is not emitted when the user presses the Home or End key (T314728),
- * but is emitted for Ctrl/Cmd+Home and Ctrl/Cmd+End.
- *
- * @property {KeyboardEvent}
- */
- "keydown",
- /**
- * When the input value changes via direct use of the input
- *
- * @property {InputEvent} event
- */
- "input",
- /**
- * When an input value change is committed by the user (e.g. on blur)
- *
- * @property {Event} event
- */
- "change",
- /**
- * When the input comes into focus
- *
- * @property {FocusEvent} event
- */
- "focus",
- /**
- * When the input loses focus
- *
- * @property {FocusEvent} event
- */
- "blur",
- /**
- * When the input value is cleared through the use of the clear button
- *
- * @property {MouseEvent} event
- */
- "clear"
- ],
- setup(e, { emit: t, attrs: n }) {
- const a = n.id, {
- computedDisabled: i,
- computedStatus: r,
- computedInputId: o
- } = Ne(
- U(e, "disabled"),
- U(e, "status"),
- a
- ), u = pe(gt, void 0), s = Fe(U(e, "modelValue"), t), d = h(() => e.clearable && !!s.value && !i.value), p = h(() => ({
- "cdx-text-input--has-start-icon": !!e.startIcon,
- "cdx-text-input--has-end-icon": !!e.endIcon,
- "cdx-text-input--clearable": d.value,
- ["cdx-text-input--status-".concat(r.value)]: !0
- })), {
- rootClasses: g,
- rootStyle: y,
- otherAttrs: M
- } = ge(n, p), x = h(() => {
- const D = M.value, { id: $ } = D;
- return ae(D, ["id"]);
- }), T = h(() => ({
- "cdx-text-input__input--has-value": !!s.value
- }));
- return {
- computedInputId: o,
- descriptionId: u,
- wrappedModel: s,
- isClearable: d,
- rootClasses: g,
- rootStyle: y,
- otherAttrsMinusId: x,
- inputClasses: T,
- computedDisabled: i,
- onClear: ($) => {
- s.value = "", t("clear", $);
- },
- onInput: ($) => {
- t("input", $);
- },
- onChange: ($) => {
- t("change", $);
- },
- onKeydown: ($) => {
- ($.key === "Home" || $.key === "End") && !$.ctrlKey && !$.metaKey || t("keydown", $);
- },
- onFocus: ($) => {
- t("focus", $);
- },
- onBlur: ($) => {
- t("blur", $);
- },
- cdxIconClear: tt
- };
- },
- // Public methods
- // These must be in the methods block, not in the setup function, otherwise their documentation
- // won't be picked up by vue-docgen
- methods: {
- /**
- * Focus the component's input element.
- *
- * @public
- */
- focus() {
- this.$refs.input.focus();
- },
- /**
- * Blur the component's input element.
- *
- * @public
- */
- blur() {
- this.$refs.input.blur();
- }
- }
-});
-const _n = ["id", "type", "aria-describedby", "disabled"];
-function wn(e, t, n, a, i, r) {
- const o = A("cdx-icon");
- return v(), m("div", {
- class: K(["cdx-text-input", e.rootClasses]),
- style: oe(e.rootStyle)
- }, [
- Ke(_("input", X({
- id: e.computedInputId,
- ref: "input",
- "onUpdate:modelValue": t[0] || (t[0] = (u) => e.wrappedModel = u),
- class: ["cdx-text-input__input", e.inputClasses]
- }, e.otherAttrsMinusId, {
- type: e.inputType,
- "aria-describedby": e.descriptionId,
- disabled: e.computedDisabled,
- size: "1",
- onInput: t[1] || (t[1] = (...u) => e.onInput && e.onInput(...u)),
- onChange: t[2] || (t[2] = (...u) => e.onChange && e.onChange(...u)),
- onFocus: t[3] || (t[3] = (...u) => e.onFocus && e.onFocus(...u)),
- onBlur: t[4] || (t[4] = (...u) => e.onBlur && e.onBlur(...u)),
- onKeydown: t[5] || (t[5] = (...u) => e.onKeydown && e.onKeydown(...u))
- }), null, 16, _n), [
- [Ge, e.wrappedModel]
- ]),
- e.startIcon ? (v(), R(o, {
- key: 0,
- icon: e.startIcon,
- class: "cdx-text-input__icon-vue cdx-text-input__start-icon"
- }, null, 8, ["icon"])) : k("", !0),
- e.endIcon ? (v(), R(o, {
- key: 1,
- icon: e.endIcon,
- class: "cdx-text-input__icon-vue cdx-text-input__end-icon"
- }, null, 8, ["icon"])) : k("", !0),
- e.isClearable ? (v(), R(o, {
- key: 2,
- icon: e.cdxIconClear,
- class: "cdx-text-input__icon-vue cdx-text-input__clear-icon",
- onMousedown: t[6] || (t[6] = we(() => {
- }, ["prevent"])),
- onClick: e.onClear
- }, null, 8, ["icon", "onClick"])) : k("", !0)
- ], 6);
-}
-const In = /* @__PURE__ */ N(Sn, [["render", wn]]), xn = z(Ae), kn = F({
- name: "CdxSearchInput",
- components: {
- CdxButton: yn,
- CdxTextInput: In
- },
- /**
- * Attributes, besides class, will be passed to the TextInput's input element.
- */
- inheritAttrs: !1,
- props: {
- /**
- * Value of the search input, provided by `v-model` binding in the parent component.
- */
- modelValue: {
- type: [String, Number],
- default: ""
- },
- /**
- * Submit button text.
- *
- * If this is provided, a submit button with this label will be added.
- */
- buttonLabel: {
- type: String,
- default: ""
- },
- /**
- * Whether the search input is disabled.
- */
- disabled: {
- type: Boolean,
- default: !1
- },
- /**
- * `status` property of the TextInput component
- */
- status: {
- type: String,
- default: "default",
- validator: xn
- }
- },
- emits: [
- /**
- * When the input value changes
- *
- * @property {string | number} value The new value
- */
- "update:modelValue",
- /**
- * When the submit button is clicked.
- *
- * @property {string | number} value The current input
- */
- "submit-click",
- /**
- * When the input value changes via direct use of the input
- *
- * @property {InputEvent} event
- */
- "input",
- /**
- * When an input value change is committed by the user (e.g. on blur)
- *
- * @property {Event} event
- */
- "change",
- /**
- * When the input comes into focus
- *
- * @property {FocusEvent} event
- */
- "focus",
- /**
- * When the input loses focus
- *
- * @property {FocusEvent} event
- */
- "blur"
- ],
- setup(e, { emit: t, attrs: n }) {
- const a = Fe(U(e, "modelValue"), t), { computedDisabled: i } = Ne(U(e, "disabled")), r = h(() => ({
- "cdx-search-input--has-end-button": !!e.buttonLabel
- })), {
- rootClasses: o,
- rootStyle: u,
- otherAttrs: s
- } = ge(n, r);
- return {
- wrappedModel: a,
- computedDisabled: i,
- rootClasses: o,
- rootStyle: u,
- otherAttrs: s,
- handleSubmit: () => {
- t("submit-click", a.value);
- },
- searchIcon: at
- };
- },
- methods: {
- /**
- * Focus the component's input element.
- *
- * @public
- */
- focus() {
- this.$refs.textInput.focus();
- }
- }
-});
-const Mn = { class: "cdx-search-input__input-wrapper" };
-function Tn(e, t, n, a, i, r) {
- const o = A("cdx-text-input"), u = A("cdx-button");
- return v(), m("div", {
- class: K(["cdx-search-input", e.rootClasses]),
- style: oe(e.rootStyle)
- }, [
- _("div", Mn, [
- G(o, X({
- ref: "textInput",
- modelValue: e.wrappedModel,
- "onUpdate:modelValue": t[0] || (t[0] = (s) => e.wrappedModel = s),
- class: "cdx-search-input__text-input",
- "input-type": "search",
- "start-icon": e.searchIcon,
- disabled: e.computedDisabled,
- status: e.status
- }, e.otherAttrs, {
- onKeydown: _e(e.handleSubmit, ["enter"]),
- onInput: t[1] || (t[1] = (s) => e.$emit("input", s)),
- onChange: t[2] || (t[2] = (s) => e.$emit("change", s)),
- onFocus: t[3] || (t[3] = (s) => e.$emit("focus", s)),
- onBlur: t[4] || (t[4] = (s) => e.$emit("blur", s))
- }), null, 16, ["modelValue", "start-icon", "disabled", "status", "onKeydown"]),
- E(e.$slots, "default")
- ]),
- e.buttonLabel ? (v(), R(u, {
- key: 0,
- class: "cdx-search-input__end-button",
- disabled: e.computedDisabled,
- onClick: e.handleSubmit
- }, {
- default: q(() => [
- le(L(e.buttonLabel), 1)
- ]),
- _: 1
- }, 8, ["disabled", "onClick"])) : k("", !0)
- ], 6);
-}
-const Bn = /* @__PURE__ */ N(kn, [["render", Tn]]), Vn = F({
- name: "CdxTypeaheadSearch",
- components: {
- CdxIcon: ve,
- CdxMenu: un,
- CdxSearchInput: Bn
- },
- /**
- * Attributes, besides class, will be passed to the TextInput's input element.
- */
- inheritAttrs: !1,
- props: {
- /**
- * ID attribute for the form.
- */
- id: {
- type: String,
- required: !0
- },
- /**
- * Action attribute for form.
- */
- formAction: {
- type: String,
- required: !0
- },
- /**
- * Label attribute for the list of search results.
- */
- searchResultsLabel: {
- type: String,
- required: !0
- },
- /**
- * List of search results. See the SearchResult type.
- */
- searchResults: {
- type: Array,
- required: !0
- },
- /**
- * Label for the submit button.
- *
- * If no label is provided, the submit button will not be displayed.
- */
- buttonLabel: {
- type: String,
- default: ""
- },
- /**
- * Initial value for the text input.
- *
- * Triggers an initial `input` event on mount.
- */
- initialInputValue: {
- type: String,
- default: ""
- },
- /**
- * Link for the final menu item.
- *
- * This will typically be a link to the search page for the current search query.
- */
- searchFooterUrl: {
- type: String,
- default: ""
- },
- /**
- * Time interval for debouncing input events, in ms.
- */
- debounceInterval: {
- type: Number,
- default: ft
- },
- /**
- * Whether the search query should be highlighted within a search result's title.
- */
- highlightQuery: {
- type: Boolean,
- default: !1
- },
- /**
- * Whether to show search results' thumbnails (or a placeholder icon).
- */
- showThumbnail: {
- type: Boolean,
- default: !1
- },
- /**
- * Contract the width of the input when unfocused and expand the width of
- * the input when focused to accommodate the extra width of the thumbnails.
- *
- * This prop is ignored if showThumbnail is false.
- */
- autoExpandWidth: {
- type: Boolean,
- default: !1
- },
- /**
- * Limit the number of menu items to display before scrolling.
- *
- * Setting this prop to anything falsy will show all menu items.
- *
- * By default, all menu items are shown.
- */
- visibleItemLimit: {
- type: Number,
- default: null
- }
- },
- emits: [
- /**
- * When the text input value changes. Debounced by default.
- *
- * @property {string} value The new input value
- */
- "input",
- /**
- * When a search result is selected.
- *
- * @property {SearchResultClickEvent} event Data for the selected result
- */
- "search-result-click",
- /**
- * When the form is submitted.
- *
- * @property {SearchResultClickEvent} event Data for the selected result
- */
- "submit",
- /**
- * When the user scrolls towards the bottom of the menu.
- *
- * If it is possible to add or load more menu items, then now would be a good moment
- * so that the user can experience infinite scrolling.
- */
- "load-more"
- ],
- setup(e, { attrs: t, emit: n, slots: a }) {
- const i = b(), r = b(), o = De("typeahead-search-menu"), u = b(!1), s = b(!1), d = b(!1), p = b(!1), g = b(e.initialInputValue), y = b(""), M = h(() => {
- var f, C;
- return (C = (f = r.value) == null ? void 0 : f.getHighlightedMenuItem()) == null ? void 0 : C.id;
- }), x = b(null), T = h(() => ({
- "cdx-typeahead-search__menu-message--has-thumbnail": e.showThumbnail
- })), S = h(
- () => e.searchResults.find(
- (f) => f.value === x.value
- )
- ), Z = h(
- () => e.searchFooterUrl ? { value: W, url: e.searchFooterUrl } : void 0
- ), ee = h(() => ({
- "cdx-typeahead-search--show-thumbnail": e.showThumbnail,
- "cdx-typeahead-search--expanded": u.value,
- "cdx-typeahead-search--auto-expand-width": e.showThumbnail && e.autoExpandWidth
- })), {
- rootClasses: te,
- rootStyle: se,
- otherAttrs: ie
- } = ge(t, ee);
- function $(f) {
- return f;
- }
- const B = h(() => ({
- visibleItemLimit: e.visibleItemLimit,
- showThumbnail: e.showThumbnail,
- // In case search queries aren't highlighted, default to a bold label.
- boldLabel: !0,
- hideDescriptionOverflow: !0
- }));
- let D, O;
- function ue(f, C = !1) {
- S.value && S.value.label !== f && S.value.value !== f && (x.value = null), O !== void 0 && (clearTimeout(O), O = void 0), f === "" ? u.value = !1 : (s.value = !0, a["search-results-pending"] && (O = setTimeout(() => {
- p.value && (u.value = !0), d.value = !0;
- }, pt))), D !== void 0 && (clearTimeout(D), D = void 0);
- const l = () => {
- n("input", f);
- };
- C ? l() : D = setTimeout(() => {
- l();
- }, e.debounceInterval);
- }
- function H(f) {
- var C;
- if (f === W) {
- x.value = null, g.value = y.value;
- return;
- }
- x.value = f, f !== null && (g.value = S.value ? (C = S.value.label) != null ? C : String(S.value.value) : "");
- }
- function Q() {
- p.value = !0, (y.value || d.value) && (u.value = !0);
- }
- function P() {
- p.value = !1, u.value = !1;
- }
- function ne(f) {
- const c = f, { id: C } = c, l = ae(c, ["id"]);
- if (l.value === W) {
- n("search-result-click", {
- searchResult: null,
- index: e.searchResults.length,
- numberOfResults: e.searchResults.length
- });
- return;
- }
- re(l);
- }
- function re(f) {
- const C = {
- searchResult: f,
- index: e.searchResults.findIndex(
- (l) => l.value === f.value
- ),
- numberOfResults: e.searchResults.length
- };
- n("search-result-click", C);
- }
- function me(f) {
- var C;
- if (f.value === W) {
- g.value = y.value;
- return;
- }
- g.value = f.value ? (C = f.label) != null ? C : String(f.value) : "";
- }
- function ye(f) {
- var C;
- u.value = !1, (C = r.value) == null || C.clearActive(), ne(f);
- }
- function be(f) {
- if (S.value)
- re(S.value), f.stopPropagation(), window.location.assign(S.value.url), f.preventDefault();
- else {
- const C = {
- searchResult: null,
- index: -1,
- numberOfResults: e.searchResults.length
- };
- n("submit", C);
- }
- }
- function Ce(f) {
- if (!r.value || !y.value || f.key === " ")
- return;
- const C = r.value.getHighlightedMenuItem(), l = r.value.getHighlightedViaKeyboard();
- switch (f.key) {
- case "Enter":
- C && (C.value === W && l ? window.location.assign(e.searchFooterUrl) : r.value.delegateKeyNavigation(f, { prevent: !1 })), u.value = !1;
- break;
- case "Tab":
- u.value = !1;
- break;
- default:
- r.value.delegateKeyNavigation(f);
- break;
- }
- }
- return Y(() => {
- e.initialInputValue && ue(e.initialInputValue, !0);
- }), J(U(e, "searchResults"), () => {
- y.value = g.value.trim(), p.value && s.value && y.value.length > 0 && (u.value = !0), O !== void 0 && (clearTimeout(O), O = void 0), s.value = !1, d.value = !1;
- }), {
- form: i,
- menu: r,
- menuId: o,
- highlightedId: M,
- selection: x,
- menuMessageClass: T,
- footer: Z,
- asSearchResult: $,
- inputValue: g,
- searchQuery: y,
- expanded: u,
- showPending: d,
- rootClasses: te,
- rootStyle: se,
- otherAttrs: ie,
- menuConfig: B,
- onUpdateInputValue: ue,
- onUpdateMenuSelection: H,
- onFocus: Q,
- onBlur: P,
- onSearchResultClick: ne,
- onSearchResultKeyboardNavigation: me,
- onSearchFooterClick: ye,
- onSubmit: be,
- onKeydown: Ce,
- MenuFooterValue: W,
- articleIcon: et
- };
- },
- methods: {
- /**
- * Focus the component's input element.
- *
- * @public
- */
- focus() {
- this.$refs.searchInput.focus();
- }
- }
-});
-const Ln = ["id", "action"], Kn = { class: "cdx-typeahead-search__menu-message__text" }, An = { class: "cdx-typeahead-search__menu-message__text" }, Rn = ["href", "onClickCapture"], Dn = { class: "cdx-menu-item__text cdx-typeahead-search__search-footer__text" }, En = { class: "cdx-typeahead-search__search-footer__query" };
-function Fn(e, t, n, a, i, r) {
- const o = A("cdx-icon"), u = A("cdx-menu"), s = A("cdx-search-input");
- return v(), m("div", {
- class: K(["cdx-typeahead-search", e.rootClasses]),
- style: oe(e.rootStyle)
- }, [
- _("form", {
- id: e.id,
- ref: "form",
- class: "cdx-typeahead-search__form",
- action: e.formAction,
- onSubmit: t[4] || (t[4] = (...d) => e.onSubmit && e.onSubmit(...d))
- }, [
- G(s, X({
- ref: "searchInput",
- modelValue: e.inputValue,
- "onUpdate:modelValue": t[3] || (t[3] = (d) => e.inputValue = d),
- "button-label": e.buttonLabel
- }, e.otherAttrs, {
- class: "cdx-typeahead-search__input",
- name: "search",
- role: "combobox",
- autocomplete: "off",
- "aria-autocomplete": "list",
- "aria-controls": e.menuId,
- "aria-expanded": e.expanded,
- "aria-activedescendant": e.highlightedId,
- "onUpdate:modelValue": e.onUpdateInputValue,
- onFocus: e.onFocus,
- onBlur: e.onBlur,
- onKeydown: e.onKeydown
- }), {
- default: q(() => [
- G(u, X({
- id: e.menuId,
- ref: "menu",
- expanded: e.expanded,
- "onUpdate:expanded": t[0] || (t[0] = (d) => e.expanded = d),
- class: "cdx-typeahead-search__menu",
- "show-pending": e.showPending,
- selected: e.selection,
- "menu-items": e.searchResults,
- footer: e.footer,
- "search-query": e.highlightQuery ? e.searchQuery : "",
- "show-no-results-slot": e.searchQuery.length > 0 && e.searchResults.length === 0 && e.$slots["search-no-results-text"] && e.$slots["search-no-results-text"]().length > 0
- }, e.menuConfig, {
- "aria-label": e.searchResultsLabel,
- "onUpdate:selected": e.onUpdateMenuSelection,
- onMenuItemClick: t[1] || (t[1] = (d) => e.onSearchResultClick(e.asSearchResult(d))),
- onMenuItemKeyboardNavigation: e.onSearchResultKeyboardNavigation,
- onLoadMore: t[2] || (t[2] = (d) => e.$emit("load-more"))
- }), {
- pending: q(() => [
- _("div", {
- class: K(["cdx-menu-item__content cdx-typeahead-search__menu-message", e.menuMessageClass])
- }, [
- _("span", Kn, [
- E(e.$slots, "search-results-pending")
- ])
- ], 2)
- ]),
- "no-results": q(() => [
- _("div", {
- class: K(["cdx-menu-item__content cdx-typeahead-search__menu-message", e.menuMessageClass])
- }, [
- _("span", An, [
- E(e.$slots, "search-no-results-text")
- ])
- ], 2)
- ]),
- default: q(({ menuItem: d, active: p }) => [
- d.value === e.MenuFooterValue ? (v(), m("a", {
- key: 0,
- class: K(["cdx-menu-item__content cdx-typeahead-search__search-footer", {
- "cdx-typeahead-search__search-footer__active": p
- }]),
- href: e.asSearchResult(d).url,
- onClickCapture: we((g) => e.onSearchFooterClick(e.asSearchResult(d)), ["stop"])
- }, [
- G(o, {
- class: "cdx-menu-item__thumbnail cdx-typeahead-search__search-footer__icon",
- icon: e.articleIcon
- }, null, 8, ["icon"]),
- _("span", Dn, [
- E(e.$slots, "search-footer-text", { searchQuery: e.searchQuery }, () => [
- _("strong", En, L(e.searchQuery), 1)
- ])
- ])
- ], 42, Rn)) : k("", !0)
- ]),
- _: 3
- }, 16, ["id", "expanded", "show-pending", "selected", "menu-items", "footer", "search-query", "show-no-results-slot", "aria-label", "onUpdate:selected", "onMenuItemKeyboardNavigation"])
- ]),
- _: 3
- }, 16, ["modelValue", "button-label", "aria-controls", "aria-expanded", "aria-activedescendant", "onUpdate:modelValue", "onFocus", "onBlur", "onKeydown"]),
- E(e.$slots, "default")
- ], 40, Ln)
- ], 6);
-}
-const Hn = /* @__PURE__ */ N(Vn, [["render", Fn]]);
-export {
- Hn as CdxTypeaheadSearch
-};
diff --git a/resources/lib/codex/codex-search.style-experimental-rtl.css b/resources/lib/codex/codex-search.style-experimental-rtl.css
deleted file mode 100644
index 6e878bb45697..000000000000
--- a/resources/lib/codex/codex-search.style-experimental-rtl.css
+++ /dev/null
@@ -1 +0,0 @@
-.cdx-icon{color:var(--color-base);display:inline-flex;align-items:center;justify-content:center;vertical-align:text-bottom}.cdx-icon svg{fill:currentcolor;width:100%;height:100%}.cdx-icon--x-small{min-width:12px;min-height:12px;width:.75em;height:.75em}.cdx-icon--small{min-width:16px;min-height:16px;width:1em;height:1em}.cdx-icon--medium{min-width:20px;min-height:20px;width:1.25em;height:1.25em}.cdx-icon--flipped svg{transform:scaleX(-1)}.cdx-thumbnail{display:inline-flex}.cdx-thumbnail__placeholder,.cdx-thumbnail__image{background-position:center;background-repeat:no-repeat;background-size:cover;flex-shrink:0;box-sizing:border-box;min-width:40px;min-height:40px;width:2.5em;height:2.5em;border:var(--border-subtle);border-radius:2px}.cdx-thumbnail__image{display:inline-block}.cdx-thumbnail__image-enter-active{transition-property:opacity;transition-duration:.1s}.cdx-thumbnail__image-enter-from{opacity:0}.cdx-thumbnail__placeholder{background-color:var(--background-color-interactive-subtle);display:inline-flex;align-items:center;justify-content:center}.cdx-thumbnail__placeholder__icon{min-width:20px;min-height:20px;width:1.25em;height:1.25em;display:inline-block;vertical-align:text-bottom}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-thumbnail__placeholder__icon{background-position:center;background-repeat:no-repeat;background-size:max(1.25em,20px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-thumbnail__placeholder__icon{-webkit-mask-size:max(1.25em,20px);mask-size:max(1.25em,20px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-thumbnail__placeholder__icon{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');filter:invert(var(--filter-invert-icon));opacity:var(--opacity-icon-base)}.cdx-button:not(.cdx-button--weight-quiet):disabled .cdx-thumbnail__placeholder__icon,.cdx-button--weight-primary.cdx-button--action-progressive .cdx-thumbnail__placeholder__icon,.cdx-button--weight-primary.cdx-button--action-destructive .cdx-thumbnail__placeholder__icon{filter:invert(var(--filter-invert-primary-button-icon))}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-thumbnail__placeholder__icon{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');background-color:var(--color-placeholder)}}.cdx-thumbnail__placeholder__icon--vue.cdx-icon{color:var(--color-placeholder)}.cdx-search-result-title{display:inline-block;max-width:100%;font-weight:700}.cdx-search-result-title__match{font-weight:400}.cdx-menu-item{list-style:none;position:relative;padding:8px 12px;line-height:1.6;transition-property:background-color,color,border-color,box-shadow;transition-duration:.1s}.cdx-menu-item__content{display:flex;align-items:center;line-height:1.4285714;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.cdx-menu-item__content,.cdx-menu-item__content:hover{text-decoration:none}.cdx-menu-item--has-description .cdx-menu-item__content{align-items:flex-start}.cdx-menu-item__text{max-width:100%}.cdx-menu-item__text__description{display:block}.cdx-menu-item__thumbnail.cdx-thumbnail{margin-left:8px}.cdx-menu-item__icon.cdx-icon{color:inherit;margin-left:8px}.cdx-menu-item--bold-label .cdx-menu-item__text__label{font-weight:700}.cdx-menu-item--hide-description-overflow .cdx-menu-item__text{overflow:hidden}.cdx-menu-item--hide-description-overflow .cdx-menu-item__text__description{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdx-menu-item--enabled,.cdx-menu-item--enabled .cdx-menu-item__content{color:var(--color-base)}.cdx-menu-item--enabled .cdx-menu-item__text__supporting-text,.cdx-menu-item--enabled .cdx-menu-item__text__description{color:var(--color-subtle)}.cdx-menu-item--enabled.cdx-menu-item--highlighted{background-color:var(--background-color-interactive);cursor:pointer}.cdx-menu-item--enabled.cdx-menu-item--active{background-color:var(--background-color-progressive-subtle);color:var(--color-progressive)}.cdx-menu-item--enabled.cdx-menu-item--active .cdx-menu-item__content,.cdx-menu-item--enabled.cdx-menu-item--active .cdx-menu-item__text__description{color:var(--color-progressive)}.cdx-menu-item--enabled.cdx-menu-item--selected{background-color:var(--background-color-progressive-subtle)}.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted,.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted .cdx-menu-item__content,.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted .cdx-menu-item__text__description{color:var(--color-progressive)}.cdx-menu-item--disabled{color:var(--color-disabled);cursor:default}.cdx-menu-item--disabled .cdx-menu-item__text__description{color:var(--color-disabled)}.cdx-progress-bar{box-sizing:border-box;overflow-x:hidden}.cdx-progress-bar__bar{width:33.33%;height:100%}.cdx-progress-bar:not(.cdx-progress-bar--inline){position:relative;z-index:1;height:1em;max-width:none;border:var(--border-base);border-radius:9999px;box-shadow:0 2px 2px rgba(0,0,0,.2)}.cdx-progress-bar--inline{width:100%;height:.25em}.cdx-progress-bar:not(.cdx-progress-bar--disabled) .cdx-progress-bar__bar{background-color:var(--background-color-progressive);animation-name:cdx-animation-progress-bar__bar;animation-duration:1.6s;animation-timing-function:linear;animation-iteration-count:infinite}.cdx-progress-bar:not(.cdx-progress-bar--disabled).cdx-progress-bar--block{background-color:var(--background-color-base)}.cdx-progress-bar--disabled .cdx-progress-bar__bar{background-color:var(--background-color-disabled)}.cdx-progress-bar--disabled:not(.cdx-progress-bar--inline){background-color:var(--background-color-disabled-subtle)}@keyframes cdx-animation-progress-bar__bar{0%{transform:translate(100%)}to{transform:translate(-300%)}}.cdx-menu{background-color:var(--background-color-base);display:flex;flex-direction:column;position:absolute;right:0;z-index:50;box-sizing:border-box;width:100%;border:var(--border-base);border-radius:2px;box-shadow:0 2px 2px rgba(0,0,0,.2)}.cdx-menu__progress-bar.cdx-progress-bar{position:absolute;top:0}.cdx-menu__listbox{margin:0;padding:0;overflow-y:auto}.cdx-menu--has-footer .cdx-menu-item:last-of-type{position:absolute;bottom:0;box-sizing:border-box;width:100%}.cdx-menu--has-footer .cdx-menu-item:last-of-type:not(:first-of-type){border-top:var(--border-subtle)}.cdx-button{display:inline-flex;align-items:center;justify-content:center;gap:4px;box-sizing:border-box;min-width:32px;min-height:32px;max-width:28em;margin:0;border-width:1px;border-style:solid;border-radius:2px;padding-left:11px;padding-right:11px;font-family:inherit;font-size:inherit;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-transform:none;transition-property:background-color,color,border-color,box-shadow;transition-duration:.1s}.cdx-button--size-large{min-width:44px;min-height:44px;padding-left:15px;padding-right:15px}.cdx-button--icon-only{padding-left:5px;padding-right:5px}.cdx-button--icon-only.cdx-button--size-large{padding-left:11px;padding-right:11px}.cdx-button::-moz-focus-inner{border:0;padding:0}.cdx-button .cdx-button__icon,.cdx-button .cdx-icon{vertical-align:middle}.cdx-button .cdx-icon{color:inherit}.cdx-button--fake-button,.cdx-button--fake-button:hover,.cdx-button--fake-button:focus{text-decoration:none}.cdx-button:enabled,.cdx-button.cdx-button--fake-button--enabled{background-color:var(--background-color-interactive-subtle);color:var(--color-base);border-color:var(--border-color-base)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled .cdx-button__icon{background-color:var(--color-base)}}.cdx-button:enabled:hover,.cdx-button.cdx-button--fake-button--enabled:hover{background-color:var(--background-color-base);color:var(--color-base--hover);cursor:pointer}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled:hover .cdx-button__icon{background-color:var(--color-base--hover)}}.cdx-button:enabled:active,.cdx-button.cdx-button--fake-button--enabled:active,.cdx-button:enabled.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--is-active{background-color:var(--background-color-interactive);color:var(--color-emphasized);border-color:var(--border-color-interactive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled:active .cdx-button__icon,.cdx-button:enabled.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--is-active .cdx-button__icon{background-color:var(--color-emphasized)}}.cdx-button:enabled:focus,.cdx-button.cdx-button--fake-button--enabled:focus{outline:1px solid transparent}.cdx-button:enabled:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled:focus:not(:active):not(.cdx-button--is-active){border-color:var(--border-color-progressive--focus);box-shadow:inset 0 0 0 1px var(--box-shadow-color-progressive--focus)}.cdx-button:enabled.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive{color:var(--color-progressive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive .cdx-button__icon{background-color:var(--color-progressive)}}.cdx-button:enabled.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:hover{color:var(--color-progressive--hover);border-color:var(--border-color-progressive--hover)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:hover .cdx-button__icon{background-color:var(--color-progressive--hover)}}.cdx-button:enabled.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive.cdx-button--is-active{background-color:var(--background-color-progressive-subtle);color:var(--color-progressive--active);border-color:var(--border-color-progressive--active)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:var(--color-progressive--active)}}.cdx-button:enabled.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive{color:var(--color-destructive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive .cdx-button__icon{background-color:var(--color-destructive)}}.cdx-button:enabled.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:hover{color:var(--color-destructive--hover);border-color:var(--border-color-destructive--hover)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:hover .cdx-button__icon{background-color:var(--color-destructive--hover)}}.cdx-button:enabled.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive.cdx-button--is-active{background-color:var(--background-color-destructive-subtle);color:var(--color-destructive--active);border-color:var(--border-color-destructive--active)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:var(--color-destructive--active)}}.cdx-button:enabled.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:var(--border-color-destructive--focus);box-shadow:inset 0 0 0 1px var(--box-shadow-color-destructive--focus)}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive{background-color:var(--background-color-progressive);color:var(--color-inverted);border-color:var(--border-color-progressive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover{background-color:var(--background-color-progressive--hover);border-color:var(--border-color-progressive--hover)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active{background-color:var(--background-color-progressive--active);border-color:var(--border-color-progressive--active)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:focus:not(:active):not(.cdx-button--is-active){border-color:var(--border-color-progressive--focus);box-shadow:inset 0 0 0 1px var(--box-shadow-color-progressive--focus),inset 0 0 0 2px var(--box-shadow-color-inverted)}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive{background-color:var(--background-color-destructive);color:var(--color-inverted);border-color:var(--border-color-destructive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover{background-color:var(--background-color-destructive--hover);border-color:var(--border-color-destructive--hover)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active{background-color:var(--background-color-destructive--active);border-color:var(--border-color-destructive--active)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:var(--border-color-destructive--focus);box-shadow:inset 0 0 0 1px var(--box-shadow-color-destructive--focus),inset 0 0 0 2px var(--box-shadow-color-inverted)}.cdx-button:enabled.cdx-button--weight-quiet,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet{background-color:var(--background-color-transparent);border-color:var(--border-color-transparent)}.cdx-button:enabled.cdx-button--weight-quiet:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:hover{background-color:var(--background-color-button-quiet--hover)}.cdx-button:enabled.cdx-button--weight-quiet:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--is-active{background-color:var(--background-color-button-quiet--active);color:var(--color-emphasized);border-color:var(--border-color-interactive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--is-active .cdx-button__icon{background-color:var(--color-emphasized)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive{color:var(--color-progressive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive .cdx-button__icon{background-color:var(--color-progressive)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover{background-color:var(--background-color-progressive-subtle);color:var(--color-progressive--hover)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover .cdx-button__icon{background-color:var(--color-progressive--hover)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active{background-color:var(--background-color-progressive--active);color:var(--color-inverted);border-color:var(--border-color-progressive--active)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive{color:var(--color-destructive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive .cdx-button__icon{background-color:var(--color-destructive)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover{background-color:var(--background-color-destructive-subtle);color:var(--color-destructive--hover)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover .cdx-button__icon{background-color:var(--color-destructive--hover)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active{background-color:var(--background-color-destructive--active);color:var(--color-inverted);border-color:var(--border-color-destructive--active)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:var(--border-color-destructive--focus);box-shadow:inset 0 0 0 1px var(--box-shadow-color-destructive--focus)}.cdx-button:disabled,.cdx-button.cdx-button--fake-button--disabled{background-color:var(--background-color-disabled);color:var(--color-inverted);border-color:var(--border-color-transparent)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:disabled .cdx-button__icon,.cdx-button.cdx-button--fake-button--disabled .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:disabled.cdx-button--weight-quiet,.cdx-button.cdx-button--fake-button--disabled.cdx-button--weight-quiet{background-color:var(--background-color-transparent);color:var(--color-disabled)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:disabled.cdx-button--weight-quiet .cdx-button__icon,.cdx-button.cdx-button--fake-button--disabled.cdx-button--weight-quiet .cdx-button__icon{background-color:var(--color-disabled)}}.cdx-text-input{position:relative;box-sizing:border-box;min-width:256px;border-radius:2px;overflow:hidden}.cdx-text-input .cdx-text-input__start-icon{position:absolute;top:50%;min-width:20px;min-height:20px;width:1.25em;height:1.25em;transition-property:color;transition-duration:.1s;right:9px;transform:translateY(-50%)}.cdx-text-input__icon.cdx-text-input__end-icon{min-width:16px;min-height:16px;width:1em;height:1em}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-text-input__icon.cdx-text-input__end-icon{background-position:center;background-repeat:no-repeat;background-size:max(1em,16px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-text-input__icon.cdx-text-input__end-icon{-webkit-mask-size:max(1em,16px);mask-size:max(1em,16px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}.cdx-text-input__clear-icon.cdx-icon,.cdx-text-input .cdx-text-input__end-icon{position:absolute;top:50%;min-width:16px;min-height:16px;width:1em;height:1em;transition-property:color;transition-duration:.1s;left:9px;transform:translateY(-50%)}.cdx-text-input__clear-icon.cdx-icon:hover{cursor:pointer}.cdx-text-input__end-icon.cdx-icon+.cdx-text-input__clear-icon.cdx-icon{left:calc(17px + 1em)}.cdx-text-input__input{display:block;box-sizing:border-box;min-height:32px;width:100%;margin:0;border-width:1px;border-style:solid;border-radius:0;padding:4px 8px;font-family:inherit;font-size:inherit;line-height:1.375}.cdx-text-input__input:enabled{background-color:var(--background-color-base);color:var(--color-base);border-color:var(--border-color-base);box-shadow:inset 0 0 0 1px var(--box-shadow-color-transparent);transition-property:background-color,color,border-color,box-shadow;transition-duration:.25s}.cdx-text-input__input:enabled~.cdx-text-input__icon-vue{color:var(--color-placeholder)}.cdx-text-input__input:enabled~.cdx-text-input__icon{opacity:var(--opacity-icon-placeholder)}.cdx-text-input__input:enabled:hover{border-color:var(--border-color-input--hover)}.cdx-text-input__input:enabled:focus~.cdx-text-input__icon-vue,.cdx-text-input__input:enabled.cdx-text-input__input--has-value~.cdx-text-input__icon-vue{color:var(--color-base)}.cdx-text-input__input:enabled:focus~.cdx-text-input__icon,.cdx-text-input__input:enabled.cdx-text-input__input--has-value~.cdx-text-input__icon{opacity:1}.cdx-text-input__input:enabled:focus{border-color:var(--border-color-progressive--focus);box-shadow:inset 0 0 0 1px var(--box-shadow-color-progressive--focus);outline:1px solid transparent}.cdx-text-input__input:enabled:read-only{background-color:var(--background-color-interactive-subtle)}.cdx-text-input__input:disabled{background-color:var(--background-color-disabled-subtle);color:var(--color-disabled);-webkit-text-fill-color:var(--color-disabled);border-color:var(--border-color-disabled)}.cdx-text-input__input:disabled~.cdx-text-input__icon-vue{color:var(--color-disabled);pointer-events:none}.cdx-text-input__input:disabled~.cdx-text-input__icon{opacity:var(--opacity-icon-base--disabled)}.cdx-text-input__input::placeholder{color:var(--color-placeholder);opacity:1}.cdx-text-input__input::-ms-clear{display:none}.cdx-text-input__input[type=search]{-webkit-appearance:none;-moz-appearance:textfield}.cdx-text-input__input[type=search]::-webkit-search-decoration,.cdx-text-input__input[type=search]::-webkit-search-cancel-button{display:none}.cdx-text-input--has-start-icon .cdx-text-input__input{padding-right:calc(16px + 1.25em)}.cdx-text-input--has-end-icon .cdx-text-input__input,.cdx-text-input--clearable .cdx-text-input__input{padding-left:calc(16px + 1em)}.cdx-text-input--has-end-icon.cdx-text-input--clearable .cdx-text-input__input{padding-left:calc(24px + 2em)}.cdx-text-input--status-error .cdx-text-input__input:enabled{border-color:var(--border-color-error)}.cdx-text-input--status-error .cdx-text-input__input:enabled:focus{border-color:var(--border-color-progressive--focus)}.cdx-search-input--has-end-button{background-color:var(--background-color-base);display:flex;border:var(--border-base);border-radius:2px}.cdx-search-input--has-end-button .cdx-search-input__input-wrapper{flex-grow:1;margin:-1px}.cdx-search-input--has-end-button .cdx-search-input__input-wrapper .cdx-text-input{border-top-left-radius:0;border-bottom-left-radius:0}.cdx-search-input__end-button.cdx-button{flex-shrink:0;margin:-1px 0 -1px -1px;border-top-right-radius:0;border-bottom-right-radius:0}.cdx-search-input__end-button.cdx-button:hover,.cdx-search-input__end-button.cdx-button:focus{z-index:1}.cdx-search-input__input-wrapper{position:relative}.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{min-width:20px;min-height:20px;width:1.25em;height:1.25em;display:inline-block;vertical-align:text-bottom}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{background-position:center;background-repeat:no-repeat;background-size:max(1.25em,20px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{-webkit-mask-size:max(1.25em,20px);mask-size:max(1.25em,20px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');filter:invert(var(--filter-invert-icon));opacity:var(--opacity-icon-base)}.cdx-button:not(.cdx-button--weight-quiet):disabled .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon,.cdx-button--weight-primary.cdx-button--action-progressive .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon,.cdx-button--weight-primary.cdx-button--action-destructive .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{filter:invert(var(--filter-invert-primary-button-icon))}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');background-color:var(--color-base)}}.cdx-typeahead-search__menu.cdx-menu{border-top-right-radius:0;border-top-left-radius:0}.cdx-typeahead-search .cdx-menu-item{padding:0}.cdx-typeahead-search .cdx-menu-item__content{padding:8px 12px}.cdx-typeahead-search__search-footer.cdx-menu-item{box-sizing:border-box;min-height:56px}.cdx-typeahead-search__search-footer.cdx-menu-item:visited{color:var(--color-base)}.cdx-typeahead-search__search-footer.cdx-menu-item:hover{text-decoration:none;cursor:pointer}.cdx-typeahead-search__search-footer__icon.cdx-icon{color:var(--color-subtle)}.cdx-typeahead-search__search-footer__active.cdx-menu-item .cdx-typeahead-search__search-footer__icon.cdx-icon,.cdx-typeahead-search__search-footer__active.cdx-menu-item .cdx-typeahead-search__search-footer__text{color:var(--color-progressive)}.cdx-typeahead-search .cdx-typeahead-search__menu-message--has-thumbnail{padding-right:20px}.cdx-typeahead-search--expanded .cdx-typeahead-search__input.cdx-search-input .cdx-text-input{border-bottom-right-radius:0;border-bottom-left-radius:0}.cdx-typeahead-search .cdx-text-input--has-start-icon .cdx-text-input__input{padding-right:36px}.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width:not(.cdx-typeahead-search--expanded){margin-right:24px}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width),.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded{margin-right:0}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width) .cdx-text-input__input,.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded .cdx-text-input__input{padding-right:60px}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width) .cdx-text-input__start-icon,.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded .cdx-text-input__start-icon{position:absolute;top:50%;min-width:20px;min-height:20px;width:1.25em;height:1.25em;transition-property:color;transition-duration:.1s;right:22px;transform:translateY(-50%)}.cdx-typeahead-search--show-thumbnail .cdx-typeahead-search__search-footer__icon{flex-shrink:0;min-width:40px;width:2.5em}.cdx-typeahead-search .cdx-menu-item:first-child .cdx-typeahead-search__search-footer{border-top:unset}
diff --git a/resources/lib/codex/codex-search.style-experimental.css b/resources/lib/codex/codex-search.style-experimental.css
deleted file mode 100644
index 3b9020442237..000000000000
--- a/resources/lib/codex/codex-search.style-experimental.css
+++ /dev/null
@@ -1 +0,0 @@
-.cdx-icon{color:var(--color-base);display:inline-flex;align-items:center;justify-content:center;vertical-align:text-bottom}.cdx-icon svg{fill:currentcolor;width:100%;height:100%}.cdx-icon--x-small{min-width:12px;min-height:12px;width:.75em;height:.75em}.cdx-icon--small{min-width:16px;min-height:16px;width:1em;height:1em}.cdx-icon--medium{min-width:20px;min-height:20px;width:1.25em;height:1.25em}.cdx-icon--flipped svg{transform:scaleX(-1)}.cdx-thumbnail{display:inline-flex}.cdx-thumbnail__placeholder,.cdx-thumbnail__image{background-position:center;background-repeat:no-repeat;background-size:cover;flex-shrink:0;box-sizing:border-box;min-width:40px;min-height:40px;width:2.5em;height:2.5em;border:var(--border-subtle);border-radius:2px}.cdx-thumbnail__image{display:inline-block}.cdx-thumbnail__image-enter-active{transition-property:opacity;transition-duration:.1s}.cdx-thumbnail__image-enter-from{opacity:0}.cdx-thumbnail__placeholder{background-color:var(--background-color-interactive-subtle);display:inline-flex;align-items:center;justify-content:center}.cdx-thumbnail__placeholder__icon{min-width:20px;min-height:20px;width:1.25em;height:1.25em;display:inline-block;vertical-align:text-bottom}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-thumbnail__placeholder__icon{background-position:center;background-repeat:no-repeat;background-size:max(1.25em,20px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-thumbnail__placeholder__icon{-webkit-mask-size:max(1.25em,20px);mask-size:max(1.25em,20px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-thumbnail__placeholder__icon{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');filter:invert(var(--filter-invert-icon));opacity:var(--opacity-icon-base)}.cdx-button:not(.cdx-button--weight-quiet):disabled .cdx-thumbnail__placeholder__icon,.cdx-button--weight-primary.cdx-button--action-progressive .cdx-thumbnail__placeholder__icon,.cdx-button--weight-primary.cdx-button--action-destructive .cdx-thumbnail__placeholder__icon{filter:invert(var(--filter-invert-primary-button-icon))}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-thumbnail__placeholder__icon{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');background-color:var(--color-placeholder)}}.cdx-thumbnail__placeholder__icon--vue.cdx-icon{color:var(--color-placeholder)}.cdx-search-result-title{display:inline-block;max-width:100%;font-weight:700}.cdx-search-result-title__match{font-weight:400}.cdx-menu-item{list-style:none;position:relative;padding:8px 12px;line-height:1.6;transition-property:background-color,color,border-color,box-shadow;transition-duration:.1s}.cdx-menu-item__content{display:flex;align-items:center;line-height:1.4285714;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.cdx-menu-item__content,.cdx-menu-item__content:hover{text-decoration:none}.cdx-menu-item--has-description .cdx-menu-item__content{align-items:flex-start}.cdx-menu-item__text{max-width:100%}.cdx-menu-item__text__description{display:block}.cdx-menu-item__thumbnail.cdx-thumbnail{margin-right:8px}.cdx-menu-item__icon.cdx-icon{color:inherit;margin-right:8px}.cdx-menu-item--bold-label .cdx-menu-item__text__label{font-weight:700}.cdx-menu-item--hide-description-overflow .cdx-menu-item__text{overflow:hidden}.cdx-menu-item--hide-description-overflow .cdx-menu-item__text__description{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdx-menu-item--enabled,.cdx-menu-item--enabled .cdx-menu-item__content{color:var(--color-base)}.cdx-menu-item--enabled .cdx-menu-item__text__supporting-text,.cdx-menu-item--enabled .cdx-menu-item__text__description{color:var(--color-subtle)}.cdx-menu-item--enabled.cdx-menu-item--highlighted{background-color:var(--background-color-interactive);cursor:pointer}.cdx-menu-item--enabled.cdx-menu-item--active{background-color:var(--background-color-progressive-subtle);color:var(--color-progressive)}.cdx-menu-item--enabled.cdx-menu-item--active .cdx-menu-item__content,.cdx-menu-item--enabled.cdx-menu-item--active .cdx-menu-item__text__description{color:var(--color-progressive)}.cdx-menu-item--enabled.cdx-menu-item--selected{background-color:var(--background-color-progressive-subtle)}.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted,.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted .cdx-menu-item__content,.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted .cdx-menu-item__text__description{color:var(--color-progressive)}.cdx-menu-item--disabled{color:var(--color-disabled);cursor:default}.cdx-menu-item--disabled .cdx-menu-item__text__description{color:var(--color-disabled)}.cdx-progress-bar{box-sizing:border-box;overflow-x:hidden}.cdx-progress-bar__bar{width:33.33%;height:100%}.cdx-progress-bar:not(.cdx-progress-bar--inline){position:relative;z-index:1;height:1em;max-width:none;border:var(--border-base);border-radius:9999px;box-shadow:0 2px 2px rgba(0,0,0,.2)}.cdx-progress-bar--inline{width:100%;height:.25em}.cdx-progress-bar:not(.cdx-progress-bar--disabled) .cdx-progress-bar__bar{background-color:var(--background-color-progressive);animation-name:cdx-animation-progress-bar__bar;animation-duration:1.6s;animation-timing-function:linear;animation-iteration-count:infinite}.cdx-progress-bar:not(.cdx-progress-bar--disabled).cdx-progress-bar--block{background-color:var(--background-color-base)}.cdx-progress-bar--disabled .cdx-progress-bar__bar{background-color:var(--background-color-disabled)}.cdx-progress-bar--disabled:not(.cdx-progress-bar--inline){background-color:var(--background-color-disabled-subtle)}@keyframes cdx-animation-progress-bar__bar{0%{transform:translate(-100%)}to{transform:translate(300%)}}.cdx-menu{background-color:var(--background-color-base);display:flex;flex-direction:column;position:absolute;left:0;z-index:50;box-sizing:border-box;width:100%;border:var(--border-base);border-radius:2px;box-shadow:0 2px 2px rgba(0,0,0,.2)}.cdx-menu__progress-bar.cdx-progress-bar{position:absolute;top:0}.cdx-menu__listbox{margin:0;padding:0;overflow-y:auto}.cdx-menu--has-footer .cdx-menu-item:last-of-type{position:absolute;bottom:0;box-sizing:border-box;width:100%}.cdx-menu--has-footer .cdx-menu-item:last-of-type:not(:first-of-type){border-top:var(--border-subtle)}.cdx-button{display:inline-flex;align-items:center;justify-content:center;gap:4px;box-sizing:border-box;min-width:32px;min-height:32px;max-width:28em;margin:0;border-width:1px;border-style:solid;border-radius:2px;padding-right:11px;padding-left:11px;font-family:inherit;font-size:inherit;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-transform:none;transition-property:background-color,color,border-color,box-shadow;transition-duration:.1s}.cdx-button--size-large{min-width:44px;min-height:44px;padding-right:15px;padding-left:15px}.cdx-button--icon-only{padding-right:5px;padding-left:5px}.cdx-button--icon-only.cdx-button--size-large{padding-right:11px;padding-left:11px}.cdx-button::-moz-focus-inner{border:0;padding:0}.cdx-button .cdx-button__icon,.cdx-button .cdx-icon{vertical-align:middle}.cdx-button .cdx-icon{color:inherit}.cdx-button--fake-button,.cdx-button--fake-button:hover,.cdx-button--fake-button:focus{text-decoration:none}.cdx-button:enabled,.cdx-button.cdx-button--fake-button--enabled{background-color:var(--background-color-interactive-subtle);color:var(--color-base);border-color:var(--border-color-base)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled .cdx-button__icon{background-color:var(--color-base)}}.cdx-button:enabled:hover,.cdx-button.cdx-button--fake-button--enabled:hover{background-color:var(--background-color-base);color:var(--color-base--hover);cursor:pointer}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled:hover .cdx-button__icon{background-color:var(--color-base--hover)}}.cdx-button:enabled:active,.cdx-button.cdx-button--fake-button--enabled:active,.cdx-button:enabled.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--is-active{background-color:var(--background-color-interactive);color:var(--color-emphasized);border-color:var(--border-color-interactive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled:active .cdx-button__icon,.cdx-button:enabled.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--is-active .cdx-button__icon{background-color:var(--color-emphasized)}}.cdx-button:enabled:focus,.cdx-button.cdx-button--fake-button--enabled:focus{outline:1px solid transparent}.cdx-button:enabled:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled:focus:not(:active):not(.cdx-button--is-active){border-color:var(--border-color-progressive--focus);box-shadow:inset 0 0 0 1px var(--box-shadow-color-progressive--focus)}.cdx-button:enabled.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive{color:var(--color-progressive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive .cdx-button__icon{background-color:var(--color-progressive)}}.cdx-button:enabled.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:hover{color:var(--color-progressive--hover);border-color:var(--border-color-progressive--hover)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:hover .cdx-button__icon{background-color:var(--color-progressive--hover)}}.cdx-button:enabled.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive.cdx-button--is-active{background-color:var(--background-color-progressive-subtle);color:var(--color-progressive--active);border-color:var(--border-color-progressive--active)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:var(--color-progressive--active)}}.cdx-button:enabled.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive{color:var(--color-destructive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive .cdx-button__icon{background-color:var(--color-destructive)}}.cdx-button:enabled.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:hover{color:var(--color-destructive--hover);border-color:var(--border-color-destructive--hover)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:hover .cdx-button__icon{background-color:var(--color-destructive--hover)}}.cdx-button:enabled.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive.cdx-button--is-active{background-color:var(--background-color-destructive-subtle);color:var(--color-destructive--active);border-color:var(--border-color-destructive--active)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:var(--color-destructive--active)}}.cdx-button:enabled.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:var(--border-color-destructive--focus);box-shadow:inset 0 0 0 1px var(--box-shadow-color-destructive--focus)}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive{background-color:var(--background-color-progressive);color:var(--color-inverted);border-color:var(--border-color-progressive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover{background-color:var(--background-color-progressive--hover);border-color:var(--border-color-progressive--hover)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active{background-color:var(--background-color-progressive--active);border-color:var(--border-color-progressive--active)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:focus:not(:active):not(.cdx-button--is-active){border-color:var(--border-color-progressive--focus);box-shadow:inset 0 0 0 1px var(--box-shadow-color-progressive--focus),inset 0 0 0 2px var(--box-shadow-color-inverted)}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive{background-color:var(--background-color-destructive);color:var(--color-inverted);border-color:var(--border-color-destructive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover{background-color:var(--background-color-destructive--hover);border-color:var(--border-color-destructive--hover)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active{background-color:var(--background-color-destructive--active);border-color:var(--border-color-destructive--active)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:var(--border-color-destructive--focus);box-shadow:inset 0 0 0 1px var(--box-shadow-color-destructive--focus),inset 0 0 0 2px var(--box-shadow-color-inverted)}.cdx-button:enabled.cdx-button--weight-quiet,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet{background-color:var(--background-color-transparent);border-color:var(--border-color-transparent)}.cdx-button:enabled.cdx-button--weight-quiet:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:hover{background-color:var(--background-color-button-quiet--hover)}.cdx-button:enabled.cdx-button--weight-quiet:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--is-active{background-color:var(--background-color-button-quiet--active);color:var(--color-emphasized);border-color:var(--border-color-interactive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--is-active .cdx-button__icon{background-color:var(--color-emphasized)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive{color:var(--color-progressive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive .cdx-button__icon{background-color:var(--color-progressive)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover{background-color:var(--background-color-progressive-subtle);color:var(--color-progressive--hover)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover .cdx-button__icon{background-color:var(--color-progressive--hover)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active{background-color:var(--background-color-progressive--active);color:var(--color-inverted);border-color:var(--border-color-progressive--active)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive{color:var(--color-destructive)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive .cdx-button__icon{background-color:var(--color-destructive)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover{background-color:var(--background-color-destructive-subtle);color:var(--color-destructive--hover)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover .cdx-button__icon{background-color:var(--color-destructive--hover)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active{background-color:var(--background-color-destructive--active);color:var(--color-inverted);border-color:var(--border-color-destructive--active)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:var(--border-color-destructive--focus);box-shadow:inset 0 0 0 1px var(--box-shadow-color-destructive--focus)}.cdx-button:disabled,.cdx-button.cdx-button--fake-button--disabled{background-color:var(--background-color-disabled);color:var(--color-inverted);border-color:var(--border-color-transparent)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:disabled .cdx-button__icon,.cdx-button.cdx-button--fake-button--disabled .cdx-button__icon{background-color:var(--color-inverted)}}.cdx-button:disabled.cdx-button--weight-quiet,.cdx-button.cdx-button--fake-button--disabled.cdx-button--weight-quiet{background-color:var(--background-color-transparent);color:var(--color-disabled)}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:disabled.cdx-button--weight-quiet .cdx-button__icon,.cdx-button.cdx-button--fake-button--disabled.cdx-button--weight-quiet .cdx-button__icon{background-color:var(--color-disabled)}}.cdx-text-input{position:relative;box-sizing:border-box;min-width:256px;border-radius:2px;overflow:hidden}.cdx-text-input .cdx-text-input__start-icon{position:absolute;top:50%;min-width:20px;min-height:20px;width:1.25em;height:1.25em;transition-property:color;transition-duration:.1s;left:9px;transform:translateY(-50%)}.cdx-text-input__icon.cdx-text-input__end-icon{min-width:16px;min-height:16px;width:1em;height:1em}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-text-input__icon.cdx-text-input__end-icon{background-position:center;background-repeat:no-repeat;background-size:max(1em,16px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-text-input__icon.cdx-text-input__end-icon{-webkit-mask-size:max(1em,16px);mask-size:max(1em,16px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}.cdx-text-input__clear-icon.cdx-icon,.cdx-text-input .cdx-text-input__end-icon{position:absolute;top:50%;min-width:16px;min-height:16px;width:1em;height:1em;transition-property:color;transition-duration:.1s;right:9px;transform:translateY(-50%)}.cdx-text-input__clear-icon.cdx-icon:hover{cursor:pointer}.cdx-text-input__end-icon.cdx-icon+.cdx-text-input__clear-icon.cdx-icon{right:calc(17px + 1em)}.cdx-text-input__input{display:block;box-sizing:border-box;min-height:32px;width:100%;margin:0;border-width:1px;border-style:solid;border-radius:0;padding:4px 8px;font-family:inherit;font-size:inherit;line-height:1.375}.cdx-text-input__input:enabled{background-color:var(--background-color-base);color:var(--color-base);border-color:var(--border-color-base);box-shadow:inset 0 0 0 1px var(--box-shadow-color-transparent);transition-property:background-color,color,border-color,box-shadow;transition-duration:.25s}.cdx-text-input__input:enabled~.cdx-text-input__icon-vue{color:var(--color-placeholder)}.cdx-text-input__input:enabled~.cdx-text-input__icon{opacity:var(--opacity-icon-placeholder)}.cdx-text-input__input:enabled:hover{border-color:var(--border-color-input--hover)}.cdx-text-input__input:enabled:focus~.cdx-text-input__icon-vue,.cdx-text-input__input:enabled.cdx-text-input__input--has-value~.cdx-text-input__icon-vue{color:var(--color-base)}.cdx-text-input__input:enabled:focus~.cdx-text-input__icon,.cdx-text-input__input:enabled.cdx-text-input__input--has-value~.cdx-text-input__icon{opacity:1}.cdx-text-input__input:enabled:focus{border-color:var(--border-color-progressive--focus);box-shadow:inset 0 0 0 1px var(--box-shadow-color-progressive--focus);outline:1px solid transparent}.cdx-text-input__input:enabled:read-only{background-color:var(--background-color-interactive-subtle)}.cdx-text-input__input:disabled{background-color:var(--background-color-disabled-subtle);color:var(--color-disabled);-webkit-text-fill-color:var(--color-disabled);border-color:var(--border-color-disabled)}.cdx-text-input__input:disabled~.cdx-text-input__icon-vue{color:var(--color-disabled);pointer-events:none}.cdx-text-input__input:disabled~.cdx-text-input__icon{opacity:var(--opacity-icon-base--disabled)}.cdx-text-input__input::placeholder{color:var(--color-placeholder);opacity:1}.cdx-text-input__input::-ms-clear{display:none}.cdx-text-input__input[type=search]{-webkit-appearance:none;-moz-appearance:textfield}.cdx-text-input__input[type=search]::-webkit-search-decoration,.cdx-text-input__input[type=search]::-webkit-search-cancel-button{display:none}.cdx-text-input--has-start-icon .cdx-text-input__input{padding-left:calc(16px + 1.25em)}.cdx-text-input--has-end-icon .cdx-text-input__input,.cdx-text-input--clearable .cdx-text-input__input{padding-right:calc(16px + 1em)}.cdx-text-input--has-end-icon.cdx-text-input--clearable .cdx-text-input__input{padding-right:calc(24px + 2em)}.cdx-text-input--status-error .cdx-text-input__input:enabled{border-color:var(--border-color-error)}.cdx-text-input--status-error .cdx-text-input__input:enabled:focus{border-color:var(--border-color-progressive--focus)}.cdx-search-input--has-end-button{background-color:var(--background-color-base);display:flex;border:var(--border-base);border-radius:2px}.cdx-search-input--has-end-button .cdx-search-input__input-wrapper{flex-grow:1;margin:-1px}.cdx-search-input--has-end-button .cdx-search-input__input-wrapper .cdx-text-input{border-top-right-radius:0;border-bottom-right-radius:0}.cdx-search-input__end-button.cdx-button{flex-shrink:0;margin:-1px -1px -1px 0;border-top-left-radius:0;border-bottom-left-radius:0}.cdx-search-input__end-button.cdx-button:hover,.cdx-search-input__end-button.cdx-button:focus{z-index:1}.cdx-search-input__input-wrapper{position:relative}.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{min-width:20px;min-height:20px;width:1.25em;height:1.25em;display:inline-block;vertical-align:text-bottom}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{background-position:center;background-repeat:no-repeat;background-size:max(1.25em,20px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{-webkit-mask-size:max(1.25em,20px);mask-size:max(1.25em,20px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');filter:invert(var(--filter-invert-icon));opacity:var(--opacity-icon-base)}.cdx-button:not(.cdx-button--weight-quiet):disabled .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon,.cdx-button--weight-primary.cdx-button--action-progressive .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon,.cdx-button--weight-primary.cdx-button--action-destructive .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{filter:invert(var(--filter-invert-primary-button-icon))}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');background-color:var(--color-base)}}.cdx-typeahead-search__menu.cdx-menu{border-top-left-radius:0;border-top-right-radius:0}.cdx-typeahead-search .cdx-menu-item{padding:0}.cdx-typeahead-search .cdx-menu-item__content{padding:8px 12px}.cdx-typeahead-search__search-footer.cdx-menu-item{box-sizing:border-box;min-height:56px}.cdx-typeahead-search__search-footer.cdx-menu-item:visited{color:var(--color-base)}.cdx-typeahead-search__search-footer.cdx-menu-item:hover{text-decoration:none;cursor:pointer}.cdx-typeahead-search__search-footer__icon.cdx-icon{color:var(--color-subtle)}.cdx-typeahead-search__search-footer__active.cdx-menu-item .cdx-typeahead-search__search-footer__icon.cdx-icon,.cdx-typeahead-search__search-footer__active.cdx-menu-item .cdx-typeahead-search__search-footer__text{color:var(--color-progressive)}.cdx-typeahead-search .cdx-typeahead-search__menu-message--has-thumbnail{padding-left:20px}.cdx-typeahead-search--expanded .cdx-typeahead-search__input.cdx-search-input .cdx-text-input{border-bottom-left-radius:0;border-bottom-right-radius:0}.cdx-typeahead-search .cdx-text-input--has-start-icon .cdx-text-input__input{padding-left:36px}.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width:not(.cdx-typeahead-search--expanded){margin-left:24px}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width),.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded{margin-left:0}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width) .cdx-text-input__input,.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded .cdx-text-input__input{padding-left:60px}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width) .cdx-text-input__start-icon,.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded .cdx-text-input__start-icon{position:absolute;top:50%;min-width:20px;min-height:20px;width:1.25em;height:1.25em;transition-property:color;transition-duration:.1s;left:22px;transform:translateY(-50%)}.cdx-typeahead-search--show-thumbnail .cdx-typeahead-search__search-footer__icon{flex-shrink:0;min-width:40px;width:2.5em}.cdx-typeahead-search .cdx-menu-item:first-child .cdx-typeahead-search__search-footer{border-top:unset}
diff --git a/resources/lib/codex/codex-search.style-legacy-rtl.css b/resources/lib/codex/codex-search.style-legacy-rtl.css
deleted file mode 100644
index 156a29bca463..000000000000
--- a/resources/lib/codex/codex-search.style-legacy-rtl.css
+++ /dev/null
@@ -1 +0,0 @@
-.cdx-icon{color:#202122;display:inline-flex;align-items:center;justify-content:center;vertical-align:text-bottom}.cdx-icon svg{fill:currentcolor;width:100%;height:100%}.cdx-icon--x-small{min-width:12px;min-height:12px;width:.8571429em;height:.8571429em}.cdx-icon--small{min-width:16px;min-height:16px;width:1.1428571em;height:1.1428571em}.cdx-icon--medium{min-width:20px;min-height:20px;width:1.4285714em;height:1.4285714em}.cdx-icon--flipped svg{transform:scaleX(-1)}.cdx-thumbnail{display:inline-flex}.cdx-thumbnail__placeholder,.cdx-thumbnail__image{background-position:center;background-repeat:no-repeat;background-size:cover;flex-shrink:0;box-sizing:border-box;min-width:40px;min-height:40px;width:2.8571429em;height:2.8571429em;border:1px solid #c8ccd1;border-radius:2px}.cdx-thumbnail__image{display:inline-block}.cdx-thumbnail__image-enter-active{transition-property:opacity;transition-duration:.1s}.cdx-thumbnail__image-enter-from{opacity:0}.cdx-thumbnail__placeholder{background-color:#f8f9fa;display:inline-flex;align-items:center;justify-content:center}.cdx-thumbnail__placeholder__icon{min-width:20px;min-height:20px;width:1.4285714em;height:1.4285714em;display:inline-block;vertical-align:text-bottom}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-thumbnail__placeholder__icon{background-position:center;background-repeat:no-repeat;background-size:max(1.4285714em,20px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-thumbnail__placeholder__icon{-webkit-mask-size:max(1.4285714em,20px);mask-size:max(1.4285714em,20px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-thumbnail__placeholder__icon{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');filter:invert(0);opacity:.87}.cdx-button:not(.cdx-button--weight-quiet):disabled .cdx-thumbnail__placeholder__icon,.cdx-button--weight-primary.cdx-button--action-progressive .cdx-thumbnail__placeholder__icon,.cdx-button--weight-primary.cdx-button--action-destructive .cdx-thumbnail__placeholder__icon{filter:invert(1)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-thumbnail__placeholder__icon{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');background-color:#72777d}}.cdx-thumbnail__placeholder__icon--vue.cdx-icon{color:#72777d}.cdx-search-result-title{display:inline-block;max-width:100%;font-weight:700}.cdx-search-result-title__match{font-weight:400}.cdx-menu-item{list-style:none;position:relative;padding:8px 12px;line-height:1.6;transition-property:background-color,color,border-color,box-shadow;transition-duration:.1s}.cdx-menu-item__content{display:flex;align-items:center;line-height:1.4285714;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.cdx-menu-item__content,.cdx-menu-item__content:hover{text-decoration:none}.cdx-menu-item--has-description .cdx-menu-item__content{align-items:flex-start}.cdx-menu-item__text{max-width:100%}.cdx-menu-item__text__description{display:block}.cdx-menu-item__thumbnail.cdx-thumbnail{margin-left:8px}.cdx-menu-item__icon.cdx-icon{color:inherit;margin-left:8px}.cdx-menu-item--bold-label .cdx-menu-item__text__label{font-weight:700}.cdx-menu-item--hide-description-overflow .cdx-menu-item__text{overflow:hidden}.cdx-menu-item--hide-description-overflow .cdx-menu-item__text__description{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdx-menu-item--enabled,.cdx-menu-item--enabled .cdx-menu-item__content{color:#202122}.cdx-menu-item--enabled .cdx-menu-item__text__supporting-text,.cdx-menu-item--enabled .cdx-menu-item__text__description{color:#54595d}.cdx-menu-item--enabled.cdx-menu-item--highlighted{background-color:#eaecf0;cursor:pointer}.cdx-menu-item--enabled.cdx-menu-item--active{background-color:#eaf3ff;color:#36c}.cdx-menu-item--enabled.cdx-menu-item--active .cdx-menu-item__content,.cdx-menu-item--enabled.cdx-menu-item--active .cdx-menu-item__text__description{color:#36c}.cdx-menu-item--enabled.cdx-menu-item--selected{background-color:#eaf3ff}.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted,.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted .cdx-menu-item__content,.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted .cdx-menu-item__text__description{color:#36c}.cdx-menu-item--disabled{color:#72777d;cursor:default}.cdx-menu-item--disabled .cdx-menu-item__text__description{color:#72777d}.cdx-progress-bar{box-sizing:border-box;overflow-x:hidden}.cdx-progress-bar__bar{width:33.33%;height:100%}.cdx-progress-bar:not(.cdx-progress-bar--inline){position:relative;z-index:1;height:1.1428571em;max-width:none;border:1px solid #a2a9b1;border-radius:9999px;box-shadow:0 2px 2px rgba(0,0,0,.2)}.cdx-progress-bar--inline{width:100%;height:.2857143em}.cdx-progress-bar:not(.cdx-progress-bar--disabled) .cdx-progress-bar__bar{background-color:#36c;animation-name:cdx-animation-progress-bar__bar;animation-duration:1.6s;animation-timing-function:linear;animation-iteration-count:infinite}.cdx-progress-bar:not(.cdx-progress-bar--disabled).cdx-progress-bar--block{background-color:#fff}.cdx-progress-bar--disabled .cdx-progress-bar__bar{background-color:#c8ccd1}.cdx-progress-bar--disabled:not(.cdx-progress-bar--inline){background-color:#eaecf0}@keyframes cdx-animation-progress-bar__bar{0%{transform:translate(100%)}to{transform:translate(-300%)}}.cdx-menu{background-color:#fff;display:flex;flex-direction:column;position:absolute;right:0;z-index:50;box-sizing:border-box;width:100%;border:1px solid #a2a9b1;border-radius:2px;box-shadow:0 2px 2px rgba(0,0,0,.2)}.cdx-menu__progress-bar.cdx-progress-bar{position:absolute;top:0}.cdx-menu__listbox{margin:0;padding:0;overflow-y:auto}.cdx-menu--has-footer .cdx-menu-item:last-of-type{position:absolute;bottom:0;box-sizing:border-box;width:100%}.cdx-menu--has-footer .cdx-menu-item:last-of-type:not(:first-of-type){border-top:1px solid #c8ccd1}.cdx-button{display:inline-flex;align-items:center;justify-content:center;gap:4px;box-sizing:border-box;min-width:32px;min-height:32px;max-width:32em;margin:0;border-width:1px;border-style:solid;border-radius:2px;padding-left:11px;padding-right:11px;font-family:inherit;font-size:inherit;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-transform:none;transition-property:background-color,color,border-color,box-shadow;transition-duration:.1s}.cdx-button--size-large{min-width:44px;min-height:44px;padding-left:15px;padding-right:15px}.cdx-button--icon-only{padding-left:5px;padding-right:5px}.cdx-button--icon-only.cdx-button--size-large{padding-left:11px;padding-right:11px}.cdx-button::-moz-focus-inner{border:0;padding:0}.cdx-button .cdx-button__icon,.cdx-button .cdx-icon{vertical-align:middle}.cdx-button .cdx-icon{color:inherit}.cdx-button--fake-button,.cdx-button--fake-button:hover,.cdx-button--fake-button:focus{text-decoration:none}.cdx-button:enabled,.cdx-button.cdx-button--fake-button--enabled{background-color:#f8f9fa;color:#202122;border-color:#a2a9b1}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled .cdx-button__icon{background-color:#202122}}.cdx-button:enabled:hover,.cdx-button.cdx-button--fake-button--enabled:hover{background-color:#fff;color:#404244;cursor:pointer}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled:hover .cdx-button__icon{background-color:#404244}}.cdx-button:enabled:active,.cdx-button.cdx-button--fake-button--enabled:active,.cdx-button:enabled.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--is-active{background-color:#eaecf0;color:#000;border-color:#72777d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled:active .cdx-button__icon,.cdx-button:enabled.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--is-active .cdx-button__icon{background-color:#000}}.cdx-button:enabled:focus,.cdx-button.cdx-button--fake-button--enabled:focus{outline:1px solid transparent}.cdx-button:enabled:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled:focus:not(:active):not(.cdx-button--is-active){border-color:#36c;box-shadow:inset 0 0 0 1px #36c}.cdx-button:enabled.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive{color:#36c}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive .cdx-button__icon{background-color:#36c}}.cdx-button:enabled.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:hover{color:#447ff5;border-color:#447ff5}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:hover .cdx-button__icon{background-color:#447ff5}}.cdx-button:enabled.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive.cdx-button--is-active{background-color:#eaf3ff;color:#2a4b8d;border-color:#2a4b8d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:#2a4b8d}}.cdx-button:enabled.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive{color:#d73333}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive .cdx-button__icon{background-color:#d73333}}.cdx-button:enabled.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:hover{color:#ff4242;border-color:#ff4242}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:hover .cdx-button__icon{background-color:#ff4242}}.cdx-button:enabled.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive.cdx-button--is-active{background-color:#fee7e6;color:#b32424;border-color:#b32424}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:#b32424}}.cdx-button:enabled.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:#d73333;box-shadow:inset 0 0 0 1px #d73333}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive{background-color:#36c;color:#fff;border-color:#36c}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover{background-color:#447ff5;border-color:#447ff5}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active{background-color:#2a4b8d;border-color:#2a4b8d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:focus:not(:active):not(.cdx-button--is-active){border-color:#36c;box-shadow:inset 0 0 0 1px #36c,inset 0 0 0 2px #fff}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive{background-color:#d73333;color:#fff;border-color:#d73333}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover{background-color:#ff4242;border-color:#ff4242}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active{background-color:#b32424;border-color:#b32424}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:#d73333;box-shadow:inset 0 0 0 1px #d73333,inset 0 0 0 2px #fff}.cdx-button:enabled.cdx-button--weight-quiet,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet{background-color:rgba(255,255,255,0);border-color:transparent}.cdx-button:enabled.cdx-button--weight-quiet:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:hover{background-color:rgba(0,24,73,.027)}.cdx-button:enabled.cdx-button--weight-quiet:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--is-active{background-color:rgba(0,24,73,.082);color:#000;border-color:#72777d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--is-active .cdx-button__icon{background-color:#000}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive{color:#36c}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive .cdx-button__icon{background-color:#36c}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover{background-color:#eaf3ff;color:#447ff5}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover .cdx-button__icon{background-color:#447ff5}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active{background-color:#2a4b8d;color:#fff;border-color:#2a4b8d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive{color:#d73333}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive .cdx-button__icon{background-color:#d73333}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover{background-color:#fee7e6;color:#ff4242}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover .cdx-button__icon{background-color:#ff4242}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active{background-color:#b32424;color:#fff;border-color:#b32424}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:#d73333;box-shadow:inset 0 0 0 1px #d73333}.cdx-button:disabled,.cdx-button.cdx-button--fake-button--disabled{background-color:#c8ccd1;color:#fff;border-color:transparent}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:disabled .cdx-button__icon,.cdx-button.cdx-button--fake-button--disabled .cdx-button__icon{background-color:#fff}}.cdx-button:disabled.cdx-button--weight-quiet,.cdx-button.cdx-button--fake-button--disabled.cdx-button--weight-quiet{background-color:rgba(255,255,255,0);color:#72777d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:disabled.cdx-button--weight-quiet .cdx-button__icon,.cdx-button.cdx-button--fake-button--disabled.cdx-button--weight-quiet .cdx-button__icon{background-color:#72777d}}.cdx-text-input{position:relative;box-sizing:border-box;min-width:256px;border-radius:2px;overflow:hidden}.cdx-text-input .cdx-text-input__start-icon{position:absolute;top:50%;min-width:20px;min-height:20px;width:1.4285714em;height:1.4285714em;transition-property:color;transition-duration:.1s;right:9px;transform:translateY(-50%)}.cdx-text-input__icon.cdx-text-input__end-icon{min-width:16px;min-height:16px;width:1.1428571em;height:1.1428571em}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-text-input__icon.cdx-text-input__end-icon{background-position:center;background-repeat:no-repeat;background-size:max(1.1428571em,16px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-text-input__icon.cdx-text-input__end-icon{-webkit-mask-size:max(1.1428571em,16px);mask-size:max(1.1428571em,16px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}.cdx-text-input__clear-icon.cdx-icon,.cdx-text-input .cdx-text-input__end-icon{position:absolute;top:50%;min-width:16px;min-height:16px;width:1.1428571em;height:1.1428571em;transition-property:color;transition-duration:.1s;left:9px;transform:translateY(-50%)}.cdx-text-input__clear-icon.cdx-icon:hover{cursor:pointer}.cdx-text-input__end-icon.cdx-icon+.cdx-text-input__clear-icon.cdx-icon{left:calc(calc(8px * 2 + 1.1428571em) + 1px)}.cdx-text-input__input{display:block;box-sizing:border-box;min-height:32px;width:100%;margin:0;border-width:1px;border-style:solid;border-radius:0;padding:4px 8px;font-family:inherit;font-size:inherit;line-height:1.375}.cdx-text-input__input:enabled{background-color:#fff;color:#202122;border-color:#a2a9b1;box-shadow:inset 0 0 0 1px transparent;transition-property:background-color,color,border-color,box-shadow;transition-duration:.25s}.cdx-text-input__input:enabled~.cdx-text-input__icon-vue{color:#72777d}.cdx-text-input__input:enabled~.cdx-text-input__icon{opacity:.51}.cdx-text-input__input:enabled:hover{border-color:#72777d}.cdx-text-input__input:enabled:focus~.cdx-text-input__icon-vue,.cdx-text-input__input:enabled.cdx-text-input__input--has-value~.cdx-text-input__icon-vue{color:#202122}.cdx-text-input__input:enabled:focus~.cdx-text-input__icon,.cdx-text-input__input:enabled.cdx-text-input__input--has-value~.cdx-text-input__icon{opacity:1}.cdx-text-input__input:enabled:focus{border-color:#36c;box-shadow:inset 0 0 0 1px #36c;outline:1px solid transparent}.cdx-text-input__input:enabled:read-only{background-color:#f8f9fa}.cdx-text-input__input:disabled{background-color:#eaecf0;color:#72777d;-webkit-text-fill-color:#72777d;border-color:#c8ccd1}.cdx-text-input__input:disabled~.cdx-text-input__icon-vue{color:#72777d;pointer-events:none}.cdx-text-input__input:disabled~.cdx-text-input__icon{opacity:.51}.cdx-text-input__input::placeholder{color:#72777d;opacity:1}.cdx-text-input__input::-ms-clear{display:none}.cdx-text-input__input[type=search]{-webkit-appearance:none;-moz-appearance:textfield}.cdx-text-input__input[type=search]::-webkit-search-decoration,.cdx-text-input__input[type=search]::-webkit-search-cancel-button{display:none}.cdx-text-input--has-start-icon .cdx-text-input__input{padding-right:calc(8px + 8px + 1.4285714em)}.cdx-text-input--has-end-icon .cdx-text-input__input,.cdx-text-input--clearable .cdx-text-input__input{padding-left:calc(8px + 8px + 1.1428571em)}.cdx-text-input--has-end-icon.cdx-text-input--clearable .cdx-text-input__input{padding-left:calc(8px + calc(8px * 2 + 1.1428571em) + 1.1428571em)}.cdx-text-input--status-error .cdx-text-input__input:enabled{border-color:#b32424}.cdx-text-input--status-error .cdx-text-input__input:enabled:focus{border-color:#36c}.cdx-search-input--has-end-button{background-color:#fff;display:flex;border:1px solid #a2a9b1;border-radius:2px}.cdx-search-input--has-end-button .cdx-search-input__input-wrapper{flex-grow:1;margin:-1px}.cdx-search-input--has-end-button .cdx-search-input__input-wrapper .cdx-text-input{border-top-left-radius:0;border-bottom-left-radius:0}.cdx-search-input__end-button.cdx-button{flex-shrink:0;margin:-1px 0 -1px -1px;border-top-right-radius:0;border-bottom-right-radius:0}.cdx-search-input__end-button.cdx-button:hover,.cdx-search-input__end-button.cdx-button:focus{z-index:1}.cdx-search-input__input-wrapper{position:relative}.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{min-width:20px;min-height:20px;width:1.4285714em;height:1.4285714em;display:inline-block;vertical-align:text-bottom}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{background-position:center;background-repeat:no-repeat;background-size:max(1.4285714em,20px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{-webkit-mask-size:max(1.4285714em,20px);mask-size:max(1.4285714em,20px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');filter:invert(0);opacity:.87}.cdx-button:not(.cdx-button--weight-quiet):disabled .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon,.cdx-button--weight-primary.cdx-button--action-progressive .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon,.cdx-button--weight-primary.cdx-button--action-destructive .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{filter:invert(1)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');background-color:#202122}}.cdx-typeahead-search__menu.cdx-menu{border-top-right-radius:0;border-top-left-radius:0}.cdx-typeahead-search .cdx-menu-item{padding:0}.cdx-typeahead-search .cdx-menu-item__content{padding:8px 12px}.cdx-typeahead-search__search-footer.cdx-menu-item{box-sizing:border-box;min-height:56px}.cdx-typeahead-search__search-footer.cdx-menu-item:visited{color:#202122}.cdx-typeahead-search__search-footer.cdx-menu-item:hover{text-decoration:none;cursor:pointer}.cdx-typeahead-search__search-footer__icon.cdx-icon{color:#54595d}.cdx-typeahead-search__search-footer__active.cdx-menu-item .cdx-typeahead-search__search-footer__icon.cdx-icon,.cdx-typeahead-search__search-footer__active.cdx-menu-item .cdx-typeahead-search__search-footer__text{color:#36c}.cdx-typeahead-search .cdx-typeahead-search__menu-message--has-thumbnail{padding-right:20px}.cdx-typeahead-search--expanded .cdx-typeahead-search__input.cdx-search-input .cdx-text-input{border-bottom-right-radius:0;border-bottom-left-radius:0}.cdx-typeahead-search .cdx-text-input--has-start-icon .cdx-text-input__input{padding-right:36px}.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width:not(.cdx-typeahead-search--expanded){margin-right:24px}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width),.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded{margin-right:0}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width) .cdx-text-input__input,.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded .cdx-text-input__input{padding-right:60px}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width) .cdx-text-input__start-icon,.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded .cdx-text-input__start-icon{position:absolute;top:50%;min-width:20px;min-height:20px;width:1.4285714em;height:1.4285714em;transition-property:color;transition-duration:.1s;right:22px;transform:translateY(-50%)}.cdx-typeahead-search--show-thumbnail .cdx-typeahead-search__search-footer__icon{flex-shrink:0;min-width:40px;width:2.8571429em}.cdx-typeahead-search .cdx-menu-item:first-child .cdx-typeahead-search__search-footer{border-top:unset}
diff --git a/resources/lib/codex/codex-search.style-legacy.css b/resources/lib/codex/codex-search.style-legacy.css
deleted file mode 100644
index 2028bec0810e..000000000000
--- a/resources/lib/codex/codex-search.style-legacy.css
+++ /dev/null
@@ -1 +0,0 @@
-.cdx-icon{color:#202122;display:inline-flex;align-items:center;justify-content:center;vertical-align:text-bottom}.cdx-icon svg{fill:currentcolor;width:100%;height:100%}.cdx-icon--x-small{min-width:12px;min-height:12px;width:.8571429em;height:.8571429em}.cdx-icon--small{min-width:16px;min-height:16px;width:1.1428571em;height:1.1428571em}.cdx-icon--medium{min-width:20px;min-height:20px;width:1.4285714em;height:1.4285714em}.cdx-icon--flipped svg{transform:scaleX(-1)}.cdx-thumbnail{display:inline-flex}.cdx-thumbnail__placeholder,.cdx-thumbnail__image{background-position:center;background-repeat:no-repeat;background-size:cover;flex-shrink:0;box-sizing:border-box;min-width:40px;min-height:40px;width:2.8571429em;height:2.8571429em;border:1px solid #c8ccd1;border-radius:2px}.cdx-thumbnail__image{display:inline-block}.cdx-thumbnail__image-enter-active{transition-property:opacity;transition-duration:.1s}.cdx-thumbnail__image-enter-from{opacity:0}.cdx-thumbnail__placeholder{background-color:#f8f9fa;display:inline-flex;align-items:center;justify-content:center}.cdx-thumbnail__placeholder__icon{min-width:20px;min-height:20px;width:1.4285714em;height:1.4285714em;display:inline-block;vertical-align:text-bottom}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-thumbnail__placeholder__icon{background-position:center;background-repeat:no-repeat;background-size:max(1.4285714em,20px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-thumbnail__placeholder__icon{-webkit-mask-size:max(1.4285714em,20px);mask-size:max(1.4285714em,20px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-thumbnail__placeholder__icon{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');filter:invert(0);opacity:.87}.cdx-button:not(.cdx-button--weight-quiet):disabled .cdx-thumbnail__placeholder__icon,.cdx-button--weight-primary.cdx-button--action-progressive .cdx-thumbnail__placeholder__icon,.cdx-button--weight-primary.cdx-button--action-destructive .cdx-thumbnail__placeholder__icon{filter:invert(1)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-thumbnail__placeholder__icon{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');background-color:#72777d}}.cdx-thumbnail__placeholder__icon--vue.cdx-icon{color:#72777d}.cdx-search-result-title{display:inline-block;max-width:100%;font-weight:700}.cdx-search-result-title__match{font-weight:400}.cdx-menu-item{list-style:none;position:relative;padding:8px 12px;line-height:1.6;transition-property:background-color,color,border-color,box-shadow;transition-duration:.1s}.cdx-menu-item__content{display:flex;align-items:center;line-height:1.4285714;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.cdx-menu-item__content,.cdx-menu-item__content:hover{text-decoration:none}.cdx-menu-item--has-description .cdx-menu-item__content{align-items:flex-start}.cdx-menu-item__text{max-width:100%}.cdx-menu-item__text__description{display:block}.cdx-menu-item__thumbnail.cdx-thumbnail{margin-right:8px}.cdx-menu-item__icon.cdx-icon{color:inherit;margin-right:8px}.cdx-menu-item--bold-label .cdx-menu-item__text__label{font-weight:700}.cdx-menu-item--hide-description-overflow .cdx-menu-item__text{overflow:hidden}.cdx-menu-item--hide-description-overflow .cdx-menu-item__text__description{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdx-menu-item--enabled,.cdx-menu-item--enabled .cdx-menu-item__content{color:#202122}.cdx-menu-item--enabled .cdx-menu-item__text__supporting-text,.cdx-menu-item--enabled .cdx-menu-item__text__description{color:#54595d}.cdx-menu-item--enabled.cdx-menu-item--highlighted{background-color:#eaecf0;cursor:pointer}.cdx-menu-item--enabled.cdx-menu-item--active{background-color:#eaf3ff;color:#36c}.cdx-menu-item--enabled.cdx-menu-item--active .cdx-menu-item__content,.cdx-menu-item--enabled.cdx-menu-item--active .cdx-menu-item__text__description{color:#36c}.cdx-menu-item--enabled.cdx-menu-item--selected{background-color:#eaf3ff}.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted,.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted .cdx-menu-item__content,.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted .cdx-menu-item__text__description{color:#36c}.cdx-menu-item--disabled{color:#72777d;cursor:default}.cdx-menu-item--disabled .cdx-menu-item__text__description{color:#72777d}.cdx-progress-bar{box-sizing:border-box;overflow-x:hidden}.cdx-progress-bar__bar{width:33.33%;height:100%}.cdx-progress-bar:not(.cdx-progress-bar--inline){position:relative;z-index:1;height:1.1428571em;max-width:none;border:1px solid #a2a9b1;border-radius:9999px;box-shadow:0 2px 2px rgba(0,0,0,.2)}.cdx-progress-bar--inline{width:100%;height:.2857143em}.cdx-progress-bar:not(.cdx-progress-bar--disabled) .cdx-progress-bar__bar{background-color:#36c;animation-name:cdx-animation-progress-bar__bar;animation-duration:1.6s;animation-timing-function:linear;animation-iteration-count:infinite}.cdx-progress-bar:not(.cdx-progress-bar--disabled).cdx-progress-bar--block{background-color:#fff}.cdx-progress-bar--disabled .cdx-progress-bar__bar{background-color:#c8ccd1}.cdx-progress-bar--disabled:not(.cdx-progress-bar--inline){background-color:#eaecf0}@keyframes cdx-animation-progress-bar__bar{0%{transform:translate(-100%)}to{transform:translate(300%)}}.cdx-menu{background-color:#fff;display:flex;flex-direction:column;position:absolute;left:0;z-index:50;box-sizing:border-box;width:100%;border:1px solid #a2a9b1;border-radius:2px;box-shadow:0 2px 2px rgba(0,0,0,.2)}.cdx-menu__progress-bar.cdx-progress-bar{position:absolute;top:0}.cdx-menu__listbox{margin:0;padding:0;overflow-y:auto}.cdx-menu--has-footer .cdx-menu-item:last-of-type{position:absolute;bottom:0;box-sizing:border-box;width:100%}.cdx-menu--has-footer .cdx-menu-item:last-of-type:not(:first-of-type){border-top:1px solid #c8ccd1}.cdx-button{display:inline-flex;align-items:center;justify-content:center;gap:4px;box-sizing:border-box;min-width:32px;min-height:32px;max-width:32em;margin:0;border-width:1px;border-style:solid;border-radius:2px;padding-right:11px;padding-left:11px;font-family:inherit;font-size:inherit;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-transform:none;transition-property:background-color,color,border-color,box-shadow;transition-duration:.1s}.cdx-button--size-large{min-width:44px;min-height:44px;padding-right:15px;padding-left:15px}.cdx-button--icon-only{padding-right:5px;padding-left:5px}.cdx-button--icon-only.cdx-button--size-large{padding-right:11px;padding-left:11px}.cdx-button::-moz-focus-inner{border:0;padding:0}.cdx-button .cdx-button__icon,.cdx-button .cdx-icon{vertical-align:middle}.cdx-button .cdx-icon{color:inherit}.cdx-button--fake-button,.cdx-button--fake-button:hover,.cdx-button--fake-button:focus{text-decoration:none}.cdx-button:enabled,.cdx-button.cdx-button--fake-button--enabled{background-color:#f8f9fa;color:#202122;border-color:#a2a9b1}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled .cdx-button__icon{background-color:#202122}}.cdx-button:enabled:hover,.cdx-button.cdx-button--fake-button--enabled:hover{background-color:#fff;color:#404244;cursor:pointer}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled:hover .cdx-button__icon{background-color:#404244}}.cdx-button:enabled:active,.cdx-button.cdx-button--fake-button--enabled:active,.cdx-button:enabled.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--is-active{background-color:#eaecf0;color:#000;border-color:#72777d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled:active .cdx-button__icon,.cdx-button:enabled.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--is-active .cdx-button__icon{background-color:#000}}.cdx-button:enabled:focus,.cdx-button.cdx-button--fake-button--enabled:focus{outline:1px solid transparent}.cdx-button:enabled:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled:focus:not(:active):not(.cdx-button--is-active){border-color:#36c;box-shadow:inset 0 0 0 1px #36c}.cdx-button:enabled.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive{color:#36c}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive .cdx-button__icon{background-color:#36c}}.cdx-button:enabled.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:hover{color:#447ff5;border-color:#447ff5}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:hover .cdx-button__icon{background-color:#447ff5}}.cdx-button:enabled.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive.cdx-button--is-active{background-color:#eaf3ff;color:#2a4b8d;border-color:#2a4b8d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:#2a4b8d}}.cdx-button:enabled.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive{color:#d73333}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive .cdx-button__icon{background-color:#d73333}}.cdx-button:enabled.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:hover{color:#ff4242;border-color:#ff4242}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:hover .cdx-button__icon{background-color:#ff4242}}.cdx-button:enabled.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive.cdx-button--is-active{background-color:#fee7e6;color:#b32424;border-color:#b32424}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:#b32424}}.cdx-button:enabled.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:#d73333;box-shadow:inset 0 0 0 1px #d73333}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive{background-color:#36c;color:#fff;border-color:#36c}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover{background-color:#447ff5;border-color:#447ff5}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active{background-color:#2a4b8d;border-color:#2a4b8d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:focus:not(:active):not(.cdx-button--is-active){border-color:#36c;box-shadow:inset 0 0 0 1px #36c,inset 0 0 0 2px #fff}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive{background-color:#d73333;color:#fff;border-color:#d73333}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover{background-color:#ff4242;border-color:#ff4242}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active{background-color:#b32424;border-color:#b32424}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:#d73333;box-shadow:inset 0 0 0 1px #d73333,inset 0 0 0 2px #fff}.cdx-button:enabled.cdx-button--weight-quiet,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet{background-color:rgba(255,255,255,0);border-color:transparent}.cdx-button:enabled.cdx-button--weight-quiet:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:hover{background-color:rgba(0,24,73,.027)}.cdx-button:enabled.cdx-button--weight-quiet:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--is-active{background-color:rgba(0,24,73,.082);color:#000;border-color:#72777d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--is-active .cdx-button__icon{background-color:#000}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive{color:#36c}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive .cdx-button__icon{background-color:#36c}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover{background-color:#eaf3ff;color:#447ff5}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover .cdx-button__icon{background-color:#447ff5}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active{background-color:#2a4b8d;color:#fff;border-color:#2a4b8d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive{color:#d73333}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive .cdx-button__icon{background-color:#d73333}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover{background-color:#fee7e6;color:#ff4242}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover .cdx-button__icon{background-color:#ff4242}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active{background-color:#b32424;color:#fff;border-color:#b32424}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:#d73333;box-shadow:inset 0 0 0 1px #d73333}.cdx-button:disabled,.cdx-button.cdx-button--fake-button--disabled{background-color:#c8ccd1;color:#fff;border-color:transparent}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:disabled .cdx-button__icon,.cdx-button.cdx-button--fake-button--disabled .cdx-button__icon{background-color:#fff}}.cdx-button:disabled.cdx-button--weight-quiet,.cdx-button.cdx-button--fake-button--disabled.cdx-button--weight-quiet{background-color:rgba(255,255,255,0);color:#72777d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:disabled.cdx-button--weight-quiet .cdx-button__icon,.cdx-button.cdx-button--fake-button--disabled.cdx-button--weight-quiet .cdx-button__icon{background-color:#72777d}}.cdx-text-input{position:relative;box-sizing:border-box;min-width:256px;border-radius:2px;overflow:hidden}.cdx-text-input .cdx-text-input__start-icon{position:absolute;top:50%;min-width:20px;min-height:20px;width:1.4285714em;height:1.4285714em;transition-property:color;transition-duration:.1s;left:9px;transform:translateY(-50%)}.cdx-text-input__icon.cdx-text-input__end-icon{min-width:16px;min-height:16px;width:1.1428571em;height:1.1428571em}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-text-input__icon.cdx-text-input__end-icon{background-position:center;background-repeat:no-repeat;background-size:max(1.1428571em,16px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-text-input__icon.cdx-text-input__end-icon{-webkit-mask-size:max(1.1428571em,16px);mask-size:max(1.1428571em,16px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}.cdx-text-input__clear-icon.cdx-icon,.cdx-text-input .cdx-text-input__end-icon{position:absolute;top:50%;min-width:16px;min-height:16px;width:1.1428571em;height:1.1428571em;transition-property:color;transition-duration:.1s;right:9px;transform:translateY(-50%)}.cdx-text-input__clear-icon.cdx-icon:hover{cursor:pointer}.cdx-text-input__end-icon.cdx-icon+.cdx-text-input__clear-icon.cdx-icon{right:calc(calc(8px * 2 + 1.1428571em) + 1px)}.cdx-text-input__input{display:block;box-sizing:border-box;min-height:32px;width:100%;margin:0;border-width:1px;border-style:solid;border-radius:0;padding:4px 8px;font-family:inherit;font-size:inherit;line-height:1.375}.cdx-text-input__input:enabled{background-color:#fff;color:#202122;border-color:#a2a9b1;box-shadow:inset 0 0 0 1px transparent;transition-property:background-color,color,border-color,box-shadow;transition-duration:.25s}.cdx-text-input__input:enabled~.cdx-text-input__icon-vue{color:#72777d}.cdx-text-input__input:enabled~.cdx-text-input__icon{opacity:.51}.cdx-text-input__input:enabled:hover{border-color:#72777d}.cdx-text-input__input:enabled:focus~.cdx-text-input__icon-vue,.cdx-text-input__input:enabled.cdx-text-input__input--has-value~.cdx-text-input__icon-vue{color:#202122}.cdx-text-input__input:enabled:focus~.cdx-text-input__icon,.cdx-text-input__input:enabled.cdx-text-input__input--has-value~.cdx-text-input__icon{opacity:1}.cdx-text-input__input:enabled:focus{border-color:#36c;box-shadow:inset 0 0 0 1px #36c;outline:1px solid transparent}.cdx-text-input__input:enabled:read-only{background-color:#f8f9fa}.cdx-text-input__input:disabled{background-color:#eaecf0;color:#72777d;-webkit-text-fill-color:#72777d;border-color:#c8ccd1}.cdx-text-input__input:disabled~.cdx-text-input__icon-vue{color:#72777d;pointer-events:none}.cdx-text-input__input:disabled~.cdx-text-input__icon{opacity:.51}.cdx-text-input__input::placeholder{color:#72777d;opacity:1}.cdx-text-input__input::-ms-clear{display:none}.cdx-text-input__input[type=search]{-webkit-appearance:none;-moz-appearance:textfield}.cdx-text-input__input[type=search]::-webkit-search-decoration,.cdx-text-input__input[type=search]::-webkit-search-cancel-button{display:none}.cdx-text-input--has-start-icon .cdx-text-input__input{padding-left:calc(8px + 8px + 1.4285714em)}.cdx-text-input--has-end-icon .cdx-text-input__input,.cdx-text-input--clearable .cdx-text-input__input{padding-right:calc(8px + 8px + 1.1428571em)}.cdx-text-input--has-end-icon.cdx-text-input--clearable .cdx-text-input__input{padding-right:calc(8px + calc(8px * 2 + 1.1428571em) + 1.1428571em)}.cdx-text-input--status-error .cdx-text-input__input:enabled{border-color:#b32424}.cdx-text-input--status-error .cdx-text-input__input:enabled:focus{border-color:#36c}.cdx-search-input--has-end-button{background-color:#fff;display:flex;border:1px solid #a2a9b1;border-radius:2px}.cdx-search-input--has-end-button .cdx-search-input__input-wrapper{flex-grow:1;margin:-1px}.cdx-search-input--has-end-button .cdx-search-input__input-wrapper .cdx-text-input{border-top-right-radius:0;border-bottom-right-radius:0}.cdx-search-input__end-button.cdx-button{flex-shrink:0;margin:-1px -1px -1px 0;border-top-left-radius:0;border-bottom-left-radius:0}.cdx-search-input__end-button.cdx-button:hover,.cdx-search-input__end-button.cdx-button:focus{z-index:1}.cdx-search-input__input-wrapper{position:relative}.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{min-width:20px;min-height:20px;width:1.4285714em;height:1.4285714em;display:inline-block;vertical-align:text-bottom}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{background-position:center;background-repeat:no-repeat;background-size:max(1.4285714em,20px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{-webkit-mask-size:max(1.4285714em,20px);mask-size:max(1.4285714em,20px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');filter:invert(0);opacity:.87}.cdx-button:not(.cdx-button--weight-quiet):disabled .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon,.cdx-button--weight-primary.cdx-button--action-progressive .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon,.cdx-button--weight-primary.cdx-button--action-destructive .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{filter:invert(1)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');background-color:#202122}}.cdx-typeahead-search__menu.cdx-menu{border-top-left-radius:0;border-top-right-radius:0}.cdx-typeahead-search .cdx-menu-item{padding:0}.cdx-typeahead-search .cdx-menu-item__content{padding:8px 12px}.cdx-typeahead-search__search-footer.cdx-menu-item{box-sizing:border-box;min-height:56px}.cdx-typeahead-search__search-footer.cdx-menu-item:visited{color:#202122}.cdx-typeahead-search__search-footer.cdx-menu-item:hover{text-decoration:none;cursor:pointer}.cdx-typeahead-search__search-footer__icon.cdx-icon{color:#54595d}.cdx-typeahead-search__search-footer__active.cdx-menu-item .cdx-typeahead-search__search-footer__icon.cdx-icon,.cdx-typeahead-search__search-footer__active.cdx-menu-item .cdx-typeahead-search__search-footer__text{color:#36c}.cdx-typeahead-search .cdx-typeahead-search__menu-message--has-thumbnail{padding-left:20px}.cdx-typeahead-search--expanded .cdx-typeahead-search__input.cdx-search-input .cdx-text-input{border-bottom-left-radius:0;border-bottom-right-radius:0}.cdx-typeahead-search .cdx-text-input--has-start-icon .cdx-text-input__input{padding-left:36px}.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width:not(.cdx-typeahead-search--expanded){margin-left:24px}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width),.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded{margin-left:0}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width) .cdx-text-input__input,.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded .cdx-text-input__input{padding-left:60px}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width) .cdx-text-input__start-icon,.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded .cdx-text-input__start-icon{position:absolute;top:50%;min-width:20px;min-height:20px;width:1.4285714em;height:1.4285714em;transition-property:color;transition-duration:.1s;left:22px;transform:translateY(-50%)}.cdx-typeahead-search--show-thumbnail .cdx-typeahead-search__search-footer__icon{flex-shrink:0;min-width:40px;width:2.8571429em}.cdx-typeahead-search .cdx-menu-item:first-child .cdx-typeahead-search__search-footer{border-top:unset}
diff --git a/resources/lib/codex/codex-search.style-rtl.css b/resources/lib/codex/codex-search.style-rtl.css
deleted file mode 100644
index 5b04eadd65bc..000000000000
--- a/resources/lib/codex/codex-search.style-rtl.css
+++ /dev/null
@@ -1 +0,0 @@
-.cdx-icon{color:#202122;display:inline-flex;align-items:center;justify-content:center;vertical-align:text-bottom}.cdx-icon svg{fill:currentcolor;width:100%;height:100%}.cdx-icon--x-small{min-width:12px;min-height:12px;width:.75em;height:.75em}.cdx-icon--small{min-width:16px;min-height:16px;width:1em;height:1em}.cdx-icon--medium{min-width:20px;min-height:20px;width:1.25em;height:1.25em}.cdx-icon--flipped svg{transform:scaleX(-1)}.cdx-thumbnail{display:inline-flex}.cdx-thumbnail__placeholder,.cdx-thumbnail__image{background-position:center;background-repeat:no-repeat;background-size:cover;flex-shrink:0;box-sizing:border-box;min-width:40px;min-height:40px;width:2.5em;height:2.5em;border:1px solid #c8ccd1;border-radius:2px}.cdx-thumbnail__image{display:inline-block}.cdx-thumbnail__image-enter-active{transition-property:opacity;transition-duration:.1s}.cdx-thumbnail__image-enter-from{opacity:0}.cdx-thumbnail__placeholder{background-color:#f8f9fa;display:inline-flex;align-items:center;justify-content:center}.cdx-thumbnail__placeholder__icon{min-width:20px;min-height:20px;width:1.25em;height:1.25em;display:inline-block;vertical-align:text-bottom}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-thumbnail__placeholder__icon{background-position:center;background-repeat:no-repeat;background-size:max(1.25em,20px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-thumbnail__placeholder__icon{-webkit-mask-size:max(1.25em,20px);mask-size:max(1.25em,20px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-thumbnail__placeholder__icon{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');filter:invert(0);opacity:.87}.cdx-button:not(.cdx-button--weight-quiet):disabled .cdx-thumbnail__placeholder__icon,.cdx-button--weight-primary.cdx-button--action-progressive .cdx-thumbnail__placeholder__icon,.cdx-button--weight-primary.cdx-button--action-destructive .cdx-thumbnail__placeholder__icon{filter:invert(1)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-thumbnail__placeholder__icon{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');background-color:#72777d}}.cdx-thumbnail__placeholder__icon--vue.cdx-icon{color:#72777d}.cdx-search-result-title{display:inline-block;max-width:100%;font-weight:700}.cdx-search-result-title__match{font-weight:400}.cdx-menu-item{list-style:none;position:relative;padding:8px 12px;line-height:1.6;transition-property:background-color,color,border-color,box-shadow;transition-duration:.1s}.cdx-menu-item__content{display:flex;align-items:center;line-height:1.4285714;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.cdx-menu-item__content,.cdx-menu-item__content:hover{text-decoration:none}.cdx-menu-item--has-description .cdx-menu-item__content{align-items:flex-start}.cdx-menu-item__text{max-width:100%}.cdx-menu-item__text__description{display:block}.cdx-menu-item__thumbnail.cdx-thumbnail{margin-left:8px}.cdx-menu-item__icon.cdx-icon{color:inherit;margin-left:8px}.cdx-menu-item--bold-label .cdx-menu-item__text__label{font-weight:700}.cdx-menu-item--hide-description-overflow .cdx-menu-item__text{overflow:hidden}.cdx-menu-item--hide-description-overflow .cdx-menu-item__text__description{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdx-menu-item--enabled,.cdx-menu-item--enabled .cdx-menu-item__content{color:#202122}.cdx-menu-item--enabled .cdx-menu-item__text__supporting-text,.cdx-menu-item--enabled .cdx-menu-item__text__description{color:#54595d}.cdx-menu-item--enabled.cdx-menu-item--highlighted{background-color:#eaecf0;cursor:pointer}.cdx-menu-item--enabled.cdx-menu-item--active{background-color:#eaf3ff;color:#36c}.cdx-menu-item--enabled.cdx-menu-item--active .cdx-menu-item__content,.cdx-menu-item--enabled.cdx-menu-item--active .cdx-menu-item__text__description{color:#36c}.cdx-menu-item--enabled.cdx-menu-item--selected{background-color:#eaf3ff}.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted,.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted .cdx-menu-item__content,.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted .cdx-menu-item__text__description{color:#36c}.cdx-menu-item--disabled{color:#72777d;cursor:default}.cdx-menu-item--disabled .cdx-menu-item__text__description{color:#72777d}.cdx-progress-bar{box-sizing:border-box;overflow-x:hidden}.cdx-progress-bar__bar{width:33.33%;height:100%}.cdx-progress-bar:not(.cdx-progress-bar--inline){position:relative;z-index:1;height:1em;max-width:none;border:1px solid #a2a9b1;border-radius:9999px;box-shadow:0 2px 2px rgba(0,0,0,.2)}.cdx-progress-bar--inline{width:100%;height:.25em}.cdx-progress-bar:not(.cdx-progress-bar--disabled) .cdx-progress-bar__bar{background-color:#36c;animation-name:cdx-animation-progress-bar__bar;animation-duration:1.6s;animation-timing-function:linear;animation-iteration-count:infinite}.cdx-progress-bar:not(.cdx-progress-bar--disabled).cdx-progress-bar--block{background-color:#fff}.cdx-progress-bar--disabled .cdx-progress-bar__bar{background-color:#c8ccd1}.cdx-progress-bar--disabled:not(.cdx-progress-bar--inline){background-color:#eaecf0}@keyframes cdx-animation-progress-bar__bar{0%{transform:translate(100%)}to{transform:translate(-300%)}}.cdx-menu{background-color:#fff;display:flex;flex-direction:column;position:absolute;right:0;z-index:50;box-sizing:border-box;width:100%;border:1px solid #a2a9b1;border-radius:2px;box-shadow:0 2px 2px rgba(0,0,0,.2)}.cdx-menu__progress-bar.cdx-progress-bar{position:absolute;top:0}.cdx-menu__listbox{margin:0;padding:0;overflow-y:auto}.cdx-menu--has-footer .cdx-menu-item:last-of-type{position:absolute;bottom:0;box-sizing:border-box;width:100%}.cdx-menu--has-footer .cdx-menu-item:last-of-type:not(:first-of-type){border-top:1px solid #c8ccd1}.cdx-button{display:inline-flex;align-items:center;justify-content:center;gap:4px;box-sizing:border-box;min-width:32px;min-height:32px;max-width:28em;margin:0;border-width:1px;border-style:solid;border-radius:2px;padding-left:11px;padding-right:11px;font-family:inherit;font-size:inherit;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-transform:none;transition-property:background-color,color,border-color,box-shadow;transition-duration:.1s}.cdx-button--size-large{min-width:44px;min-height:44px;padding-left:15px;padding-right:15px}.cdx-button--icon-only{padding-left:5px;padding-right:5px}.cdx-button--icon-only.cdx-button--size-large{padding-left:11px;padding-right:11px}.cdx-button::-moz-focus-inner{border:0;padding:0}.cdx-button .cdx-button__icon,.cdx-button .cdx-icon{vertical-align:middle}.cdx-button .cdx-icon{color:inherit}.cdx-button--fake-button,.cdx-button--fake-button:hover,.cdx-button--fake-button:focus{text-decoration:none}.cdx-button:enabled,.cdx-button.cdx-button--fake-button--enabled{background-color:#f8f9fa;color:#202122;border-color:#a2a9b1}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled .cdx-button__icon{background-color:#202122}}.cdx-button:enabled:hover,.cdx-button.cdx-button--fake-button--enabled:hover{background-color:#fff;color:#404244;cursor:pointer}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled:hover .cdx-button__icon{background-color:#404244}}.cdx-button:enabled:active,.cdx-button.cdx-button--fake-button--enabled:active,.cdx-button:enabled.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--is-active{background-color:#eaecf0;color:#000;border-color:#72777d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled:active .cdx-button__icon,.cdx-button:enabled.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--is-active .cdx-button__icon{background-color:#000}}.cdx-button:enabled:focus,.cdx-button.cdx-button--fake-button--enabled:focus{outline:1px solid transparent}.cdx-button:enabled:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled:focus:not(:active):not(.cdx-button--is-active){border-color:#36c;box-shadow:inset 0 0 0 1px #36c}.cdx-button:enabled.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive{color:#36c}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive .cdx-button__icon{background-color:#36c}}.cdx-button:enabled.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:hover{color:#447ff5;border-color:#447ff5}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:hover .cdx-button__icon{background-color:#447ff5}}.cdx-button:enabled.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive.cdx-button--is-active{background-color:#eaf3ff;color:#2a4b8d;border-color:#2a4b8d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:#2a4b8d}}.cdx-button:enabled.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive{color:#d73333}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive .cdx-button__icon{background-color:#d73333}}.cdx-button:enabled.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:hover{color:#ff4242;border-color:#ff4242}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:hover .cdx-button__icon{background-color:#ff4242}}.cdx-button:enabled.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive.cdx-button--is-active{background-color:#fee7e6;color:#b32424;border-color:#b32424}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:#b32424}}.cdx-button:enabled.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:#d73333;box-shadow:inset 0 0 0 1px #d73333}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive{background-color:#36c;color:#fff;border-color:#36c}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover{background-color:#447ff5;border-color:#447ff5}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active{background-color:#2a4b8d;border-color:#2a4b8d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:focus:not(:active):not(.cdx-button--is-active){border-color:#36c;box-shadow:inset 0 0 0 1px #36c,inset 0 0 0 2px #fff}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive{background-color:#d73333;color:#fff;border-color:#d73333}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover{background-color:#ff4242;border-color:#ff4242}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active{background-color:#b32424;border-color:#b32424}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:#d73333;box-shadow:inset 0 0 0 1px #d73333,inset 0 0 0 2px #fff}.cdx-button:enabled.cdx-button--weight-quiet,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet{background-color:rgba(255,255,255,0);border-color:transparent}.cdx-button:enabled.cdx-button--weight-quiet:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:hover{background-color:rgba(0,24,73,.027)}.cdx-button:enabled.cdx-button--weight-quiet:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--is-active{background-color:rgba(0,24,73,.082);color:#000;border-color:#72777d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--is-active .cdx-button__icon{background-color:#000}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive{color:#36c}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive .cdx-button__icon{background-color:#36c}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover{background-color:#eaf3ff;color:#447ff5}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover .cdx-button__icon{background-color:#447ff5}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active{background-color:#2a4b8d;color:#fff;border-color:#2a4b8d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive{color:#d73333}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive .cdx-button__icon{background-color:#d73333}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover{background-color:#fee7e6;color:#ff4242}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover .cdx-button__icon{background-color:#ff4242}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active{background-color:#b32424;color:#fff;border-color:#b32424}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:#d73333;box-shadow:inset 0 0 0 1px #d73333}.cdx-button:disabled,.cdx-button.cdx-button--fake-button--disabled{background-color:#c8ccd1;color:#fff;border-color:transparent}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:disabled .cdx-button__icon,.cdx-button.cdx-button--fake-button--disabled .cdx-button__icon{background-color:#fff}}.cdx-button:disabled.cdx-button--weight-quiet,.cdx-button.cdx-button--fake-button--disabled.cdx-button--weight-quiet{background-color:rgba(255,255,255,0);color:#72777d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:disabled.cdx-button--weight-quiet .cdx-button__icon,.cdx-button.cdx-button--fake-button--disabled.cdx-button--weight-quiet .cdx-button__icon{background-color:#72777d}}.cdx-text-input{position:relative;box-sizing:border-box;min-width:256px;border-radius:2px;overflow:hidden}.cdx-text-input .cdx-text-input__start-icon{position:absolute;top:50%;min-width:20px;min-height:20px;width:1.25em;height:1.25em;transition-property:color;transition-duration:.1s;right:9px;transform:translateY(-50%)}.cdx-text-input__icon.cdx-text-input__end-icon{min-width:16px;min-height:16px;width:1em;height:1em}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-text-input__icon.cdx-text-input__end-icon{background-position:center;background-repeat:no-repeat;background-size:max(1em,16px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-text-input__icon.cdx-text-input__end-icon{-webkit-mask-size:max(1em,16px);mask-size:max(1em,16px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}.cdx-text-input__clear-icon.cdx-icon,.cdx-text-input .cdx-text-input__end-icon{position:absolute;top:50%;min-width:16px;min-height:16px;width:1em;height:1em;transition-property:color;transition-duration:.1s;left:9px;transform:translateY(-50%)}.cdx-text-input__clear-icon.cdx-icon:hover{cursor:pointer}.cdx-text-input__end-icon.cdx-icon+.cdx-text-input__clear-icon.cdx-icon{left:calc(17px + 1em)}.cdx-text-input__input{display:block;box-sizing:border-box;min-height:32px;width:100%;margin:0;border-width:1px;border-style:solid;border-radius:0;padding:4px 8px;font-family:inherit;font-size:inherit;line-height:1.375}.cdx-text-input__input:enabled{background-color:#fff;color:#202122;border-color:#a2a9b1;box-shadow:inset 0 0 0 1px transparent;transition-property:background-color,color,border-color,box-shadow;transition-duration:.25s}.cdx-text-input__input:enabled~.cdx-text-input__icon-vue{color:#72777d}.cdx-text-input__input:enabled~.cdx-text-input__icon{opacity:.51}.cdx-text-input__input:enabled:hover{border-color:#72777d}.cdx-text-input__input:enabled:focus~.cdx-text-input__icon-vue,.cdx-text-input__input:enabled.cdx-text-input__input--has-value~.cdx-text-input__icon-vue{color:#202122}.cdx-text-input__input:enabled:focus~.cdx-text-input__icon,.cdx-text-input__input:enabled.cdx-text-input__input--has-value~.cdx-text-input__icon{opacity:1}.cdx-text-input__input:enabled:focus{border-color:#36c;box-shadow:inset 0 0 0 1px #36c;outline:1px solid transparent}.cdx-text-input__input:enabled:read-only{background-color:#f8f9fa}.cdx-text-input__input:disabled{background-color:#eaecf0;color:#72777d;-webkit-text-fill-color:#72777d;border-color:#c8ccd1}.cdx-text-input__input:disabled~.cdx-text-input__icon-vue{color:#72777d;pointer-events:none}.cdx-text-input__input:disabled~.cdx-text-input__icon{opacity:.51}.cdx-text-input__input::placeholder{color:#72777d;opacity:1}.cdx-text-input__input::-ms-clear{display:none}.cdx-text-input__input[type=search]{-webkit-appearance:none;-moz-appearance:textfield}.cdx-text-input__input[type=search]::-webkit-search-decoration,.cdx-text-input__input[type=search]::-webkit-search-cancel-button{display:none}.cdx-text-input--has-start-icon .cdx-text-input__input{padding-right:calc(16px + 1.25em)}.cdx-text-input--has-end-icon .cdx-text-input__input,.cdx-text-input--clearable .cdx-text-input__input{padding-left:calc(16px + 1em)}.cdx-text-input--has-end-icon.cdx-text-input--clearable .cdx-text-input__input{padding-left:calc(24px + 2em)}.cdx-text-input--status-error .cdx-text-input__input:enabled{border-color:#b32424}.cdx-text-input--status-error .cdx-text-input__input:enabled:focus{border-color:#36c}.cdx-search-input--has-end-button{background-color:#fff;display:flex;border:1px solid #a2a9b1;border-radius:2px}.cdx-search-input--has-end-button .cdx-search-input__input-wrapper{flex-grow:1;margin:-1px}.cdx-search-input--has-end-button .cdx-search-input__input-wrapper .cdx-text-input{border-top-left-radius:0;border-bottom-left-radius:0}.cdx-search-input__end-button.cdx-button{flex-shrink:0;margin:-1px 0 -1px -1px;border-top-right-radius:0;border-bottom-right-radius:0}.cdx-search-input__end-button.cdx-button:hover,.cdx-search-input__end-button.cdx-button:focus{z-index:1}.cdx-search-input__input-wrapper{position:relative}.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{min-width:20px;min-height:20px;width:1.25em;height:1.25em;display:inline-block;vertical-align:text-bottom}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{background-position:center;background-repeat:no-repeat;background-size:max(1.25em,20px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{-webkit-mask-size:max(1.25em,20px);mask-size:max(1.25em,20px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');filter:invert(0);opacity:.87}.cdx-button:not(.cdx-button--weight-quiet):disabled .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon,.cdx-button--weight-primary.cdx-button--action-progressive .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon,.cdx-button--weight-primary.cdx-button--action-destructive .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{filter:invert(1)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');background-color:#202122}}.cdx-typeahead-search__menu.cdx-menu{border-top-right-radius:0;border-top-left-radius:0}.cdx-typeahead-search .cdx-menu-item{padding:0}.cdx-typeahead-search .cdx-menu-item__content{padding:8px 12px}.cdx-typeahead-search__search-footer.cdx-menu-item{box-sizing:border-box;min-height:56px}.cdx-typeahead-search__search-footer.cdx-menu-item:visited{color:#202122}.cdx-typeahead-search__search-footer.cdx-menu-item:hover{text-decoration:none;cursor:pointer}.cdx-typeahead-search__search-footer__icon.cdx-icon{color:#54595d}.cdx-typeahead-search__search-footer__active.cdx-menu-item .cdx-typeahead-search__search-footer__icon.cdx-icon,.cdx-typeahead-search__search-footer__active.cdx-menu-item .cdx-typeahead-search__search-footer__text{color:#36c}.cdx-typeahead-search .cdx-typeahead-search__menu-message--has-thumbnail{padding-right:20px}.cdx-typeahead-search--expanded .cdx-typeahead-search__input.cdx-search-input .cdx-text-input{border-bottom-right-radius:0;border-bottom-left-radius:0}.cdx-typeahead-search .cdx-text-input--has-start-icon .cdx-text-input__input{padding-right:36px}.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width:not(.cdx-typeahead-search--expanded){margin-right:24px}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width),.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded{margin-right:0}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width) .cdx-text-input__input,.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded .cdx-text-input__input{padding-right:60px}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width) .cdx-text-input__start-icon,.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded .cdx-text-input__start-icon{position:absolute;top:50%;min-width:20px;min-height:20px;width:1.25em;height:1.25em;transition-property:color;transition-duration:.1s;right:22px;transform:translateY(-50%)}.cdx-typeahead-search--show-thumbnail .cdx-typeahead-search__search-footer__icon{flex-shrink:0;min-width:40px;width:2.5em}.cdx-typeahead-search .cdx-menu-item:first-child .cdx-typeahead-search__search-footer{border-top:unset}
diff --git a/resources/lib/codex/codex-search.style.css b/resources/lib/codex/codex-search.style.css
deleted file mode 100644
index c4c8e590d10f..000000000000
--- a/resources/lib/codex/codex-search.style.css
+++ /dev/null
@@ -1 +0,0 @@
-.cdx-icon{color:#202122;display:inline-flex;align-items:center;justify-content:center;vertical-align:text-bottom}.cdx-icon svg{fill:currentcolor;width:100%;height:100%}.cdx-icon--x-small{min-width:12px;min-height:12px;width:.75em;height:.75em}.cdx-icon--small{min-width:16px;min-height:16px;width:1em;height:1em}.cdx-icon--medium{min-width:20px;min-height:20px;width:1.25em;height:1.25em}.cdx-icon--flipped svg{transform:scaleX(-1)}.cdx-thumbnail{display:inline-flex}.cdx-thumbnail__placeholder,.cdx-thumbnail__image{background-position:center;background-repeat:no-repeat;background-size:cover;flex-shrink:0;box-sizing:border-box;min-width:40px;min-height:40px;width:2.5em;height:2.5em;border:1px solid #c8ccd1;border-radius:2px}.cdx-thumbnail__image{display:inline-block}.cdx-thumbnail__image-enter-active{transition-property:opacity;transition-duration:.1s}.cdx-thumbnail__image-enter-from{opacity:0}.cdx-thumbnail__placeholder{background-color:#f8f9fa;display:inline-flex;align-items:center;justify-content:center}.cdx-thumbnail__placeholder__icon{min-width:20px;min-height:20px;width:1.25em;height:1.25em;display:inline-block;vertical-align:text-bottom}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-thumbnail__placeholder__icon{background-position:center;background-repeat:no-repeat;background-size:max(1.25em,20px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-thumbnail__placeholder__icon{-webkit-mask-size:max(1.25em,20px);mask-size:max(1.25em,20px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-thumbnail__placeholder__icon{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');filter:invert(0);opacity:.87}.cdx-button:not(.cdx-button--weight-quiet):disabled .cdx-thumbnail__placeholder__icon,.cdx-button--weight-primary.cdx-button--action-progressive .cdx-thumbnail__placeholder__icon,.cdx-button--weight-primary.cdx-button--action-destructive .cdx-thumbnail__placeholder__icon{filter:invert(1)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-thumbnail__placeholder__icon{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/></svg>');background-color:#72777d}}.cdx-thumbnail__placeholder__icon--vue.cdx-icon{color:#72777d}.cdx-search-result-title{display:inline-block;max-width:100%;font-weight:700}.cdx-search-result-title__match{font-weight:400}.cdx-menu-item{list-style:none;position:relative;padding:8px 12px;line-height:1.6;transition-property:background-color,color,border-color,box-shadow;transition-duration:.1s}.cdx-menu-item__content{display:flex;align-items:center;line-height:1.4285714;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.cdx-menu-item__content,.cdx-menu-item__content:hover{text-decoration:none}.cdx-menu-item--has-description .cdx-menu-item__content{align-items:flex-start}.cdx-menu-item__text{max-width:100%}.cdx-menu-item__text__description{display:block}.cdx-menu-item__thumbnail.cdx-thumbnail{margin-right:8px}.cdx-menu-item__icon.cdx-icon{color:inherit;margin-right:8px}.cdx-menu-item--bold-label .cdx-menu-item__text__label{font-weight:700}.cdx-menu-item--hide-description-overflow .cdx-menu-item__text{overflow:hidden}.cdx-menu-item--hide-description-overflow .cdx-menu-item__text__description{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdx-menu-item--enabled,.cdx-menu-item--enabled .cdx-menu-item__content{color:#202122}.cdx-menu-item--enabled .cdx-menu-item__text__supporting-text,.cdx-menu-item--enabled .cdx-menu-item__text__description{color:#54595d}.cdx-menu-item--enabled.cdx-menu-item--highlighted{background-color:#eaecf0;cursor:pointer}.cdx-menu-item--enabled.cdx-menu-item--active{background-color:#eaf3ff;color:#36c}.cdx-menu-item--enabled.cdx-menu-item--active .cdx-menu-item__content,.cdx-menu-item--enabled.cdx-menu-item--active .cdx-menu-item__text__description{color:#36c}.cdx-menu-item--enabled.cdx-menu-item--selected{background-color:#eaf3ff}.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted,.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted .cdx-menu-item__content,.cdx-menu-item--enabled.cdx-menu-item--selected.cdx-menu-item--highlighted .cdx-menu-item__text__description{color:#36c}.cdx-menu-item--disabled{color:#72777d;cursor:default}.cdx-menu-item--disabled .cdx-menu-item__text__description{color:#72777d}.cdx-progress-bar{box-sizing:border-box;overflow-x:hidden}.cdx-progress-bar__bar{width:33.33%;height:100%}.cdx-progress-bar:not(.cdx-progress-bar--inline){position:relative;z-index:1;height:1em;max-width:none;border:1px solid #a2a9b1;border-radius:9999px;box-shadow:0 2px 2px rgba(0,0,0,.2)}.cdx-progress-bar--inline{width:100%;height:.25em}.cdx-progress-bar:not(.cdx-progress-bar--disabled) .cdx-progress-bar__bar{background-color:#36c;animation-name:cdx-animation-progress-bar__bar;animation-duration:1.6s;animation-timing-function:linear;animation-iteration-count:infinite}.cdx-progress-bar:not(.cdx-progress-bar--disabled).cdx-progress-bar--block{background-color:#fff}.cdx-progress-bar--disabled .cdx-progress-bar__bar{background-color:#c8ccd1}.cdx-progress-bar--disabled:not(.cdx-progress-bar--inline){background-color:#eaecf0}@keyframes cdx-animation-progress-bar__bar{0%{transform:translate(-100%)}to{transform:translate(300%)}}.cdx-menu{background-color:#fff;display:flex;flex-direction:column;position:absolute;left:0;z-index:50;box-sizing:border-box;width:100%;border:1px solid #a2a9b1;border-radius:2px;box-shadow:0 2px 2px rgba(0,0,0,.2)}.cdx-menu__progress-bar.cdx-progress-bar{position:absolute;top:0}.cdx-menu__listbox{margin:0;padding:0;overflow-y:auto}.cdx-menu--has-footer .cdx-menu-item:last-of-type{position:absolute;bottom:0;box-sizing:border-box;width:100%}.cdx-menu--has-footer .cdx-menu-item:last-of-type:not(:first-of-type){border-top:1px solid #c8ccd1}.cdx-button{display:inline-flex;align-items:center;justify-content:center;gap:4px;box-sizing:border-box;min-width:32px;min-height:32px;max-width:28em;margin:0;border-width:1px;border-style:solid;border-radius:2px;padding-right:11px;padding-left:11px;font-family:inherit;font-size:inherit;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-transform:none;transition-property:background-color,color,border-color,box-shadow;transition-duration:.1s}.cdx-button--size-large{min-width:44px;min-height:44px;padding-right:15px;padding-left:15px}.cdx-button--icon-only{padding-right:5px;padding-left:5px}.cdx-button--icon-only.cdx-button--size-large{padding-right:11px;padding-left:11px}.cdx-button::-moz-focus-inner{border:0;padding:0}.cdx-button .cdx-button__icon,.cdx-button .cdx-icon{vertical-align:middle}.cdx-button .cdx-icon{color:inherit}.cdx-button--fake-button,.cdx-button--fake-button:hover,.cdx-button--fake-button:focus{text-decoration:none}.cdx-button:enabled,.cdx-button.cdx-button--fake-button--enabled{background-color:#f8f9fa;color:#202122;border-color:#a2a9b1}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled .cdx-button__icon{background-color:#202122}}.cdx-button:enabled:hover,.cdx-button.cdx-button--fake-button--enabled:hover{background-color:#fff;color:#404244;cursor:pointer}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled:hover .cdx-button__icon{background-color:#404244}}.cdx-button:enabled:active,.cdx-button.cdx-button--fake-button--enabled:active,.cdx-button:enabled.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--is-active{background-color:#eaecf0;color:#000;border-color:#72777d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled:active .cdx-button__icon,.cdx-button:enabled.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--is-active .cdx-button__icon{background-color:#000}}.cdx-button:enabled:focus,.cdx-button.cdx-button--fake-button--enabled:focus{outline:1px solid transparent}.cdx-button:enabled:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled:focus:not(:active):not(.cdx-button--is-active){border-color:#36c;box-shadow:inset 0 0 0 1px #36c}.cdx-button:enabled.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive{color:#36c}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive .cdx-button__icon{background-color:#36c}}.cdx-button:enabled.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:hover{color:#447ff5;border-color:#447ff5}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:hover .cdx-button__icon{background-color:#447ff5}}.cdx-button:enabled.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive.cdx-button--is-active{background-color:#eaf3ff;color:#2a4b8d;border-color:#2a4b8d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:#2a4b8d}}.cdx-button:enabled.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive{color:#d73333}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive .cdx-button__icon{background-color:#d73333}}.cdx-button:enabled.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:hover{color:#ff4242;border-color:#ff4242}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:hover .cdx-button__icon{background-color:#ff4242}}.cdx-button:enabled.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive.cdx-button--is-active{background-color:#fee7e6;color:#b32424;border-color:#b32424}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:#b32424}}.cdx-button:enabled.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:#d73333;box-shadow:inset 0 0 0 1px #d73333}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive{background-color:#36c;color:#fff;border-color:#36c}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover{background-color:#447ff5;border-color:#447ff5}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:hover .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active{background-color:#2a4b8d;border-color:#2a4b8d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-progressive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-progressive:focus:not(:active):not(.cdx-button--is-active){border-color:#36c;box-shadow:inset 0 0 0 1px #36c,inset 0 0 0 2px #fff}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive{background-color:#d73333;color:#fff;border-color:#d73333}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover{background-color:#ff4242;border-color:#ff4242}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:hover .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active{background-color:#b32424;border-color:#b32424}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-primary.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-primary.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:#d73333;box-shadow:inset 0 0 0 1px #d73333,inset 0 0 0 2px #fff}.cdx-button:enabled.cdx-button--weight-quiet,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet{background-color:rgba(255,255,255,0);border-color:transparent}.cdx-button:enabled.cdx-button--weight-quiet:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:hover{background-color:rgba(0,24,73,.027)}.cdx-button:enabled.cdx-button--weight-quiet:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--is-active{background-color:rgba(0,24,73,.082);color:#000;border-color:#72777d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--is-active .cdx-button__icon{background-color:#000}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive{color:#36c}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive .cdx-button__icon{background-color:#36c}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover{background-color:#eaf3ff;color:#447ff5}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:hover .cdx-button__icon{background-color:#447ff5}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active{background-color:#2a4b8d;color:#fff;border-color:#2a4b8d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-progressive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive{color:#d73333}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive .cdx-button__icon{background-color:#d73333}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover{background-color:#fee7e6;color:#ff4242}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:hover .cdx-button__icon{background-color:#ff4242}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active{background-color:#b32424;color:#fff;border-color:#b32424}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:active .cdx-button__icon,.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon,.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive.cdx-button--is-active .cdx-button__icon{background-color:#fff}}.cdx-button:enabled.cdx-button--weight-quiet.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active),.cdx-button.cdx-button--fake-button--enabled.cdx-button--weight-quiet.cdx-button--action-destructive:focus:not(:active):not(.cdx-button--is-active){border-color:#d73333;box-shadow:inset 0 0 0 1px #d73333}.cdx-button:disabled,.cdx-button.cdx-button--fake-button--disabled{background-color:#c8ccd1;color:#fff;border-color:transparent}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:disabled .cdx-button__icon,.cdx-button.cdx-button--fake-button--disabled .cdx-button__icon{background-color:#fff}}.cdx-button:disabled.cdx-button--weight-quiet,.cdx-button.cdx-button--fake-button--disabled.cdx-button--weight-quiet{background-color:rgba(255,255,255,0);color:#72777d}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-button:disabled.cdx-button--weight-quiet .cdx-button__icon,.cdx-button.cdx-button--fake-button--disabled.cdx-button--weight-quiet .cdx-button__icon{background-color:#72777d}}.cdx-text-input{position:relative;box-sizing:border-box;min-width:256px;border-radius:2px;overflow:hidden}.cdx-text-input .cdx-text-input__start-icon{position:absolute;top:50%;min-width:20px;min-height:20px;width:1.25em;height:1.25em;transition-property:color;transition-duration:.1s;left:9px;transform:translateY(-50%)}.cdx-text-input__icon.cdx-text-input__end-icon{min-width:16px;min-height:16px;width:1em;height:1em}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-text-input__icon.cdx-text-input__end-icon{background-position:center;background-repeat:no-repeat;background-size:max(1em,16px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-text-input__icon.cdx-text-input__end-icon{-webkit-mask-size:max(1em,16px);mask-size:max(1em,16px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}.cdx-text-input__clear-icon.cdx-icon,.cdx-text-input .cdx-text-input__end-icon{position:absolute;top:50%;min-width:16px;min-height:16px;width:1em;height:1em;transition-property:color;transition-duration:.1s;right:9px;transform:translateY(-50%)}.cdx-text-input__clear-icon.cdx-icon:hover{cursor:pointer}.cdx-text-input__end-icon.cdx-icon+.cdx-text-input__clear-icon.cdx-icon{right:calc(17px + 1em)}.cdx-text-input__input{display:block;box-sizing:border-box;min-height:32px;width:100%;margin:0;border-width:1px;border-style:solid;border-radius:0;padding:4px 8px;font-family:inherit;font-size:inherit;line-height:1.375}.cdx-text-input__input:enabled{background-color:#fff;color:#202122;border-color:#a2a9b1;box-shadow:inset 0 0 0 1px transparent;transition-property:background-color,color,border-color,box-shadow;transition-duration:.25s}.cdx-text-input__input:enabled~.cdx-text-input__icon-vue{color:#72777d}.cdx-text-input__input:enabled~.cdx-text-input__icon{opacity:.51}.cdx-text-input__input:enabled:hover{border-color:#72777d}.cdx-text-input__input:enabled:focus~.cdx-text-input__icon-vue,.cdx-text-input__input:enabled.cdx-text-input__input--has-value~.cdx-text-input__icon-vue{color:#202122}.cdx-text-input__input:enabled:focus~.cdx-text-input__icon,.cdx-text-input__input:enabled.cdx-text-input__input--has-value~.cdx-text-input__icon{opacity:1}.cdx-text-input__input:enabled:focus{border-color:#36c;box-shadow:inset 0 0 0 1px #36c;outline:1px solid transparent}.cdx-text-input__input:enabled:read-only{background-color:#f8f9fa}.cdx-text-input__input:disabled{background-color:#eaecf0;color:#72777d;-webkit-text-fill-color:#72777d;border-color:#c8ccd1}.cdx-text-input__input:disabled~.cdx-text-input__icon-vue{color:#72777d;pointer-events:none}.cdx-text-input__input:disabled~.cdx-text-input__icon{opacity:.51}.cdx-text-input__input::placeholder{color:#72777d;opacity:1}.cdx-text-input__input::-ms-clear{display:none}.cdx-text-input__input[type=search]{-webkit-appearance:none;-moz-appearance:textfield}.cdx-text-input__input[type=search]::-webkit-search-decoration,.cdx-text-input__input[type=search]::-webkit-search-cancel-button{display:none}.cdx-text-input--has-start-icon .cdx-text-input__input{padding-left:calc(16px + 1.25em)}.cdx-text-input--has-end-icon .cdx-text-input__input,.cdx-text-input--clearable .cdx-text-input__input{padding-right:calc(16px + 1em)}.cdx-text-input--has-end-icon.cdx-text-input--clearable .cdx-text-input__input{padding-right:calc(24px + 2em)}.cdx-text-input--status-error .cdx-text-input__input:enabled{border-color:#b32424}.cdx-text-input--status-error .cdx-text-input__input:enabled:focus{border-color:#36c}.cdx-search-input--has-end-button{background-color:#fff;display:flex;border:1px solid #a2a9b1;border-radius:2px}.cdx-search-input--has-end-button .cdx-search-input__input-wrapper{flex-grow:1;margin:-1px}.cdx-search-input--has-end-button .cdx-search-input__input-wrapper .cdx-text-input{border-top-right-radius:0;border-bottom-right-radius:0}.cdx-search-input__end-button.cdx-button{flex-shrink:0;margin:-1px -1px -1px 0;border-top-left-radius:0;border-bottom-left-radius:0}.cdx-search-input__end-button.cdx-button:hover,.cdx-search-input__end-button.cdx-button:focus{z-index:1}.cdx-search-input__input-wrapper{position:relative}.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{min-width:20px;min-height:20px;width:1.25em;height:1.25em;display:inline-block;vertical-align:text-bottom}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{background-position:center;background-repeat:no-repeat;background-size:max(1.25em,20px)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{-webkit-mask-size:max(1.25em,20px);mask-size:max(1.25em,20px);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center}}@supports not (((-webkit-mask-image: none) or (mask-image: none))){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');filter:invert(0);opacity:.87}.cdx-button:not(.cdx-button--weight-quiet):disabled .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon,.cdx-button--weight-primary.cdx-button--action-progressive .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon,.cdx-button--weight-primary.cdx-button--action-destructive .cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{filter:invert(1)}}@supports ((-webkit-mask-image: none) or (mask-image: none)){.cdx-search-input .cdx-text-input__icon.cdx-text-input__start-icon{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" fill="%23000000"><path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/></svg>');background-color:#202122}}.cdx-typeahead-search__menu.cdx-menu{border-top-left-radius:0;border-top-right-radius:0}.cdx-typeahead-search .cdx-menu-item{padding:0}.cdx-typeahead-search .cdx-menu-item__content{padding:8px 12px}.cdx-typeahead-search__search-footer.cdx-menu-item{box-sizing:border-box;min-height:56px}.cdx-typeahead-search__search-footer.cdx-menu-item:visited{color:#202122}.cdx-typeahead-search__search-footer.cdx-menu-item:hover{text-decoration:none;cursor:pointer}.cdx-typeahead-search__search-footer__icon.cdx-icon{color:#54595d}.cdx-typeahead-search__search-footer__active.cdx-menu-item .cdx-typeahead-search__search-footer__icon.cdx-icon,.cdx-typeahead-search__search-footer__active.cdx-menu-item .cdx-typeahead-search__search-footer__text{color:#36c}.cdx-typeahead-search .cdx-typeahead-search__menu-message--has-thumbnail{padding-left:20px}.cdx-typeahead-search--expanded .cdx-typeahead-search__input.cdx-search-input .cdx-text-input{border-bottom-left-radius:0;border-bottom-right-radius:0}.cdx-typeahead-search .cdx-text-input--has-start-icon .cdx-text-input__input{padding-left:36px}.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width:not(.cdx-typeahead-search--expanded){margin-left:24px}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width),.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded{margin-left:0}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width) .cdx-text-input__input,.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded .cdx-text-input__input{padding-left:60px}.cdx-typeahead-search--show-thumbnail:not(.cdx-typeahead-search--auto-expand-width) .cdx-text-input__start-icon,.cdx-typeahead-search--show-thumbnail.cdx-typeahead-search--auto-expand-width.cdx-typeahead-search--expanded .cdx-text-input__start-icon{position:absolute;top:50%;min-width:20px;min-height:20px;width:1.25em;height:1.25em;transition-property:color;transition-duration:.1s;left:22px;transform:translateY(-50%)}.cdx-typeahead-search--show-thumbnail .cdx-typeahead-search__search-footer__icon{flex-shrink:0;min-width:40px;width:2.5em}.cdx-typeahead-search .cdx-menu-item:first-child .cdx-typeahead-search__search-footer{border-top:unset}
diff --git a/resources/lib/foreign-resources.yaml b/resources/lib/foreign-resources.yaml
index 482bd1ebdc83..c82a8bfe12ca 100644
--- a/resources/lib/foreign-resources.yaml
+++ b/resources/lib/foreign-resources.yaml
@@ -154,9 +154,6 @@ codex:
package/dist/codex.js:
package/dist/codex.umd.cjs:
package/dist/codex.*.css:
- package/dist/codex-search.cjs:
- package/dist/codex-search.js:
- package/dist/codex-search.*.css:
package/dist/modules/*: modules
package/dist/mixins/*: mixins
package/LICENSE:
@@ -353,10 +350,10 @@ oojs-router:
license: MIT
homepage: https://www.mediawiki.org/wiki/OOjs_Router
authors: OOjs Team and other contributors
- version: 0.3.0
+ version: 0.4.0
type: tar
- src: https://registry.npmjs.org/oojs-router/-/oojs-router-0.3.0.tgz
- integrity: sha384-FeAuFD6G2sGGXCWvxncx9QOTEAoq5VQdNWkXEhvi4XAoOSqe8RGms5JgmL8Fl8FB
+ src: https://registry.npmjs.org/oojs-router/-/oojs-router-0.4.0.tgz
+ integrity: sha384-do9NyRuQD+dlWmCJenCQWODQzlV8vK6H+8E4BneIsFZJ2w/V+1XL+2lLe5X6NpTJ
dest:
package/dist/oojs-router.js:
package/LICENSE:
diff --git a/resources/lib/oojs-router/oojs-router.js b/resources/lib/oojs-router/oojs-router.js
index 4ee812d0deca..b7108afaac5e 100644
--- a/resources/lib/oojs-router/oojs-router.js
+++ b/resources/lib/oojs-router/oojs-router.js
@@ -1,12 +1,12 @@
/*!
- * OOjs Router v0.3.0
+ * OOjs Router v0.4.0
* https://www.mediawiki.org/wiki/OOjs_Router
*
- * Copyright 2011-2021 OOjs Team and other contributors.
+ * Copyright 2011-2024 OOjs Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2021-09-09T23:45:47Z
+ * Date: 2024-03-01T14:46:25Z
*/
( function ( $ ) {
@@ -27,11 +27,11 @@ OO.Router = function OoRouter() {
this.enabled = true;
this.oldHash = this.getPath();
- $( window ).on( 'popstate', function () {
+ window.addEventListener( 'popstate', function () {
router.emit( 'popstate' );
} );
- $( window ).on( 'hashchange', function () {
+ window.addEventListener( 'hashchange', function () {
router.emit( 'hashchange' );
} );
diff --git a/resources/src/mediawiki.ForeignStructuredUpload.BookletLayout/BookletLayout.js b/resources/src/mediawiki.ForeignStructuredUpload.BookletLayout/BookletLayout.js
index 2c759f09cd94..91c7db93cbcc 100644
--- a/resources/src/mediawiki.ForeignStructuredUpload.BookletLayout/BookletLayout.js
+++ b/resources/src/mediawiki.ForeignStructuredUpload.BookletLayout/BookletLayout.js
@@ -2,7 +2,9 @@
( function () {
/**
- * mw.ForeignStructuredUpload.BookletLayout encapsulates the process
+ * Class that encapsulates the process of uploading a file to MediaWiki.
+ *
+ * @classdesc mw.ForeignStructuredUpload.BookletLayout encapsulates the process
* of uploading a file to MediaWiki using the mw.ForeignStructuredUpload model.
*
* var uploadDialog = new mw.Upload.Dialog( {
@@ -16,12 +18,11 @@
* windowManager.addWindows( [ uploadDialog ] );
*
* @class mw.ForeignStructuredUpload.BookletLayout
- * @uses mw.ForeignStructuredUpload
* @extends mw.Upload.BookletLayout
*
* @constructor
* @param {Object} config Configuration options
- * @cfg {string} [target] Used to choose the target repository.
+ * @param {string} [config.target] Used to choose the target repository.
* If nothing is passed, the {@link mw.ForeignUpload#property-target default} is used.
*/
mw.ForeignStructuredUpload.BookletLayout = function ( config ) {
@@ -40,6 +41,7 @@
/**
* @inheritdoc
+ * @ignore
*/
mw.ForeignStructuredUpload.BookletLayout.prototype.initialize = function () {
var booklet = this;
@@ -133,7 +135,7 @@
/**
* Returns a {@link mw.ForeignStructuredUpload mw.ForeignStructuredUpload}
- * with the {@link #cfg-target target} specified in config.
+ * with the `target` specified in config.
*
* @protected
* @return {mw.Upload}
@@ -393,9 +395,9 @@
};
/**
- * Get original date from EXIF data
+ * Get original date from EXIF data.
*
- * @param {Object} file
+ * @param {File} file
* @return {jQuery.Promise} Promise resolved with the EXIF date
*/
mw.ForeignStructuredUpload.BookletLayout.prototype.getDateFromExif = function ( file ) {
@@ -447,10 +449,10 @@
};
/**
- * Get last modified date from file
+ * Get last modified date from file.
*
- * @param {Object} file
- * @return {Object} Last modified date from file
+ * @param {File} file
+ * @return {string|undefined} Last modified date from file
*/
mw.ForeignStructuredUpload.BookletLayout.prototype.getDateFromLastModified = function ( file ) {
if ( file && file.lastModified ) {
diff --git a/resources/src/mediawiki.ForeignStructuredUpload.BookletLayout/ForeignStructuredUpload.js b/resources/src/mediawiki.ForeignStructuredUpload.BookletLayout/ForeignStructuredUpload.js
index 912c05128364..57a872da095a 100644
--- a/resources/src/mediawiki.ForeignStructuredUpload.BookletLayout/ForeignStructuredUpload.js
+++ b/resources/src/mediawiki.ForeignStructuredUpload.BookletLayout/ForeignStructuredUpload.js
@@ -11,7 +11,8 @@
* **TODO: This currently only supports uploads under CC-BY-SA 4.0,
* and should really have support for more licenses.**
*
- * @class mw.ForeignStructuredUpload
+ * @class ForeignStructuredUpload
+ * @memberof mw
* @extends mw.ForeignUpload
*
* @constructor
diff --git a/resources/src/mediawiki.Upload.BookletLayout/BookletLayout.js b/resources/src/mediawiki.Upload.BookletLayout/BookletLayout.js
index cc24d416db87..4e958f9dc0cc 100644
--- a/resources/src/mediawiki.Upload.BookletLayout/BookletLayout.js
+++ b/resources/src/mediawiki.Upload.BookletLayout/BookletLayout.js
@@ -2,7 +2,9 @@
( function () {
/**
- * mw.Upload.BookletLayout encapsulates the process of uploading a file
+ * BookletLayout class for encapsulating the process of uploading a file.
+ *
+ * @classdesc mw.Upload.BookletLayout encapsulates the process of uploading a file
* to MediaWiki using the {@link mw.Upload upload model}.
* The booklet emits events that can be used to get the stashed
* upload and the final file. It can be extended to accept
@@ -21,12 +23,12 @@
* - **Insert**: Has details on how to use the file that was uploaded.
*
* Each step has a form associated with it defined in
- * {@link #renderUploadForm renderUploadForm},
- * {@link #renderInfoForm renderInfoForm}, and
- * {@link #renderInsertForm renderInfoForm}. The
- * {@link #getFile getFile},
- * {@link #getFilename getFilename}, and
- * {@link #getText getText} methods are used to get
+ * {@link mw.Upload.BookletLayout#renderUploadForm renderUploadForm},
+ * {@link mw.Upload.BookletLayout#renderInfoForm renderInfoForm}, and
+ * {@link mw.Upload.BookletLayout#renderInsertForm renderInfoForm}. The
+ * {@link mw.Upload.BookletLayout#getFile getFile},
+ * {@link mw.Upload.BookletLayout#getFilename getFilename}, and
+ * {@link mw.Upload.BookletLayout#getText getText} methods are used to get
* the information filled in these forms, required to call
* {@link mw.Upload mw.Upload}.
*
@@ -34,34 +36,35 @@
*
* See the {@link mw.Upload.Dialog upload dialog}.
*
- * The {@link #event-fileUploaded fileUploaded},
- * and {@link #event-fileSaved fileSaved} events can
+ * The {@link mw.Upload.BookletLayout.event:fileUploaded fileUploaded},
+ * and {@link mw.Upload.BookletLayout.event:fileSaved fileSaved} events can
* be used to get details of the upload.
*
* ## Extending
*
* To extend using {@link mw.Upload mw.Upload}, override
- * {@link #renderInfoForm renderInfoForm} to render
+ * {@link mw.Upload.BookletLayout#renderInfoForm renderInfoForm} to render
* the form required for the specific use-case. Update the
- * {@link #getFilename getFilename}, and
- * {@link #getText getText} methods to return data
+ * {@link mw.Upload.BookletLayout#getFilename getFilename}, and
+ * {@link mw.Upload.BookletLayout#getText getText} methods to return data
* from your newly created form. If you added new fields you'll also have
- * to update the {@link #clear} method.
+ * to update the {@link mw.Upload.BookletLayout#clear} method.
*
* If you plan to use a different upload model, apart from what is mentioned
* above, you'll also have to override the
- * {@link #createUpload createUpload} method to
+ * {@link mw.Upload.BookletLayout#createUpload createUpload} method to
* return the new model. The {@link #saveFile saveFile}, and
- * the {@link #uploadFile uploadFile} methods need to be
+ * the {@link mw.Upload.BookletLayout#uploadFile uploadFile} methods need to be
* overridden to use the new model and data returned from the forms.
*
- * @class
+ * @class mw.Upload.BookletLayout
* @extends OO.ui.BookletLayout
*
* @constructor
- * @param {Object} config Configuration options
- * @cfg {jQuery} [$overlay] Overlay to use for widgets in the booklet
- * @cfg {string} [filekey] Sets the stashed file to finish uploading. Overrides most of the file selection process, and fetches a thumbnail from the server.
+ * @param {Object} config Configuration options; see also the config parameter for the
+ * {@link mw.Upload.BookletLayout} constructor.
+ * @param {jQuery} [config.$overlay] Overlay to use for widgets in the booklet
+ * @param {string} [config.filekey] Sets the stashed file to finish uploading. Overrides most of the file selection process, and fetches a thumbnail from the server.
*/
mw.Upload.BookletLayout = function ( config ) {
// Parent constructor
@@ -106,64 +109,70 @@
/* Events */
/**
- * Progress events for the uploaded file
+ * Progress events for the uploaded file.
*
- * @event fileUploadProgress
+ * @event mw.Upload.BookletLayout.fileUploadProgress
* @param {number} progress In percentage
* @param {Object} duration Duration object from `moment.duration()`
*/
/**
- * The file has finished uploading
+ * The file has finished uploading.
*
- * @event fileUploaded
+ * @event mw.Upload.BookletLayout.fileUploaded
*/
/**
- * The file has been saved to the database
+ * The file has been saved to the database.
*
- * @event fileSaved
- * @param {Object} imageInfo See mw.Upload#getImageInfo
+ * @event mw.Upload.BookletLayout.fileSaved
+ * @param {Object} imageInfo See {@link mw.Upload#getImageInfo}
*/
/**
- * The upload form has changed
+ * The upload form has changed.
*
- * @event uploadValid
+ * @event mw.Upload.BookletLayout.uploadValid
* @param {boolean} isValid The form is valid
*/
/**
- * The info form has changed
+ * The info form has changed.
*
- * @event infoValid
+ * @event mw.Upload.BookletLayout.infoValid
* @param {boolean} isValid The form is valid
*/
/* Properties */
/**
- * @property {OO.ui.FormLayout} uploadForm
* The form rendered in the first step to get the file object.
- * Rendered in {@link #renderUploadForm renderUploadForm}.
+ * Rendered in {@link mw.Upload.BookletLayout#renderUploadForm renderUploadForm}.
+ *
+ * @name mw.Upload.BookletLayout.prototype.uploadForm
+ * @type {OO.ui.FormLayout}
*/
/**
- * @property {OO.ui.FormLayout} infoForm
* The form rendered in the second step to get metadata.
- * Rendered in {@link #renderInfoForm renderInfoForm}
+ * Rendered in {@link mw.Upload.BookletLayout#renderInfoForm renderInfoForm}.
+ *
+ * @name mw.Upload.BookletLayout.prototype.infoForm
+ * @type {OO.ui.FormLayout}
*/
/**
- * @property {OO.ui.FormLayout} insertForm
- * The form rendered in the third step to show usage
- * Rendered in {@link #renderInsertForm renderInsertForm}
+ * The form rendered in the third step to show usage.
+ * Rendered in {@link mw.Upload.BookletLayout#renderInsertForm renderInsertForm}.
+ *
+ * @name mw.Upload.BookletLayout.prototype.insertForm
+ * @type {OO.ui.FormLayout}
*/
/* Methods */
/**
- * Initialize for a new upload
+ * Initialize for a new upload.
*
* @return {jQuery.Promise} Promise resolved when everything is initialized
*/
@@ -211,7 +220,7 @@
};
/**
- * Create a new upload model
+ * Create a new upload model.
*
* @protected
* @return {mw.Upload} Upload model
@@ -231,12 +240,12 @@
/**
* Uploads the file that was added in the upload form. Uses
- * {@link #getFile getFile} to get the HTML5
+ * {@link mw.Upload.BookletLayout#getFile getFile} to get the HTML5
* file object.
*
* @protected
- * @fires fileUploadProgress
- * @fires fileUploaded
+ * @fires mw.Upload.BookletLayout.fileUploadProgress
+ * @fires mw.Upload.BookletLayout.fileUploaded
* @return {jQuery.Promise}
*/
mw.Upload.BookletLayout.prototype.uploadFile = function () {
@@ -292,12 +301,12 @@
/**
* Saves the stash finalizes upload. Uses
- * {@link #getFilename getFilename}, and
- * {@link #getText getText} to get details from
+ * {@link mw.Upload.BookletLayout#getFilename getFilename}, and
+ * {@link mw.Upload.BookletLayout#getText getText} to get details from
* the form.
*
* @protected
- * @fires fileSaved
+ * @fires mw.Upload.BookletLayout.fileSaved
* @return {jQuery.Promise} Rejects the promise with an
* {@link OO.ui.Error error}, or resolves if the upload was successful.
*/
@@ -425,10 +434,9 @@
/**
* Renders and returns the upload form and sets the
- * {@link #uploadForm uploadForm} property.
+ * {@link mw.Upload.BookletLayout#uploadForm uploadForm} property.
*
* @protected
- * @fires selectFile
* @return {OO.ui.FormLayout}
*/
mw.Upload.BookletLayout.prototype.renderUploadForm = function () {
@@ -490,10 +498,10 @@
};
/**
- * Handle change events to the upload form
+ * Handle change events to the upload form.
*
* @protected
- * @fires uploadValid
+ * @fires mw.Upload.BookletLayout.uploadValid
*/
mw.Upload.BookletLayout.prototype.onUploadFormChange = function () {
this.emit( 'uploadValid', !!this.selectFileWidget.getValue() );
@@ -501,7 +509,7 @@
/**
* Renders and returns the information form for collecting
- * metadata and sets the {@link #infoForm infoForm}
+ * metadata and sets the {@link mw.Upload.BookletLayout#infoForm infoForm}
* property.
*
* @protected
@@ -561,10 +569,10 @@
};
/**
- * Handle change events to the info form
+ * Handle change events to the info form.
*
* @protected
- * @fires infoValid
+ * @fires mw.Upload.BookletLayout.infoValid
*/
mw.Upload.BookletLayout.prototype.onInfoFormChange = function () {
var layout = this;
@@ -580,7 +588,7 @@
/**
* Renders and returns the insert form to show file usage and
- * sets the {@link #insertForm insertForm} property.
+ * sets the {@link mw.Upload.BookletLayout#insertForm insertForm} property.
*
* @protected
* @return {OO.ui.FormLayout}
@@ -607,7 +615,7 @@
/**
* Gets the file object from the
- * {@link #uploadForm upload form}.
+ * {@link mw.Upload.BookletLayout#uploadForm upload form}.
*
* @protected
* @return {File|null}
@@ -618,7 +626,7 @@
/**
* Gets the file name from the
- * {@link #infoForm information form}.
+ * {@link mw.Upload.BookletLayout#infoForm information form}.
*
* @protected
* @return {string}
@@ -632,7 +640,7 @@
};
/**
- * Prefills the {@link #infoForm information form} with the given filename.
+ * Prefills the {@link mw.Upload.BookletLayout#infoForm information form} with the given filename.
*
* @protected
* @param {string} filename
@@ -652,7 +660,7 @@
/**
* Gets the page text from the
- * {@link #infoForm information form}.
+ * {@link mw.Upload.BookletLayout#infoForm information form}.
*
* @protected
* @return {string}
@@ -664,7 +672,7 @@
/* Setters */
/**
- * Sets the file object
+ * Sets the file object.
*
* @protected
* @param {File|null} file File to select
@@ -688,7 +696,7 @@
};
/**
- * Clear the values of all fields
+ * Clear the values of all fields.
*
* @protected
*/
diff --git a/resources/src/mediawiki.Upload.BookletLayout/mw.widgets.StashedFileWidget.js b/resources/src/mediawiki.Upload.BookletLayout/mw.widgets.StashedFileWidget.js
index 35b0e10c0e3e..353291bf658c 100644
--- a/resources/src/mediawiki.Upload.BookletLayout/mw.widgets.StashedFileWidget.js
+++ b/resources/src/mediawiki.Upload.BookletLayout/mw.widgets.StashedFileWidget.js
@@ -7,10 +7,11 @@
( function () {
/**
- * Accepts a stashed file and displays the information for purposes of
+ * @classdesc Accepts a stashed file and displays the information for purposes of
* publishing the file at the behest of the user.
*
* Example use:
+ *
* var widget = new mw.widgets.StashedFileWidget( {
* filekey: '12r9e4rugeec.ddtmmp.1.jpg',
* } );
@@ -19,19 +20,16 @@
* widget.setValue( '12r9epfbnskk.knfiy7.1.jpg' );
* widget.getValue(); // '12r9epfbnskk.knfiy7.1.jpg'
*
- * Note that this widget will not finish an upload for you. Use mw.Upload
- * and mw.Upload#setFilekey, then mw.Upload#finishStashUpload to accomplish
+ * Note that this widget will not finish an upload for you. Use {@link mw.Upload}
+ * and {@link mw.Upload#setFilekey}, then {@link mw.Upload#finishStashUpload} to accomplish
* that.
*
* @class mw.widgets.StashedFileWidget
* @extends OO.ui.Widget
- */
-
- /**
* @constructor
* @param {Object} config Configuration options
- * @cfg {string} filekey The filekey of the stashed file.
- * @cfg {Object} [api] API to use for thumbnails.
+ * @param {string} [config.filekey] The filekey of the stashed file.
+ * @param {Object} [config.api] API to use for thumbnails.
*/
mw.widgets.StashedFileWidget = function MWWStashedFileWidget( config ) {
if ( !config.api ) {
diff --git a/resources/src/mediawiki.Upload.Dialog.js b/resources/src/mediawiki.Upload.Dialog.js
index 5e07394bf35d..878a323889ce 100644
--- a/resources/src/mediawiki.Upload.Dialog.js
+++ b/resources/src/mediawiki.Upload.Dialog.js
@@ -1,7 +1,9 @@
( function () {
/**
- * mw.Upload.Dialog controls a {@link mw.Upload.BookletLayout BookletLayout}.
+ * Class for controlling a BookletLayout.
+ *
+ * @classdesc mw.Upload.Dialog controls a {@link mw.Upload.BookletLayout BookletLayout}.
*
* ## Usage
*
@@ -16,9 +18,9 @@
*
* The dialog's closing promise can be used to get details of the upload.
*
- * If you want to use a different OO.ui.BookletLayout, for example the
- * mw.ForeignStructuredUpload.BookletLayout, like in the case of of the upload
- * interface in VisualEditor, you can pass it in the {@link #cfg-bookletClass}:
+ * If you want to use a different {@link OO.ui.BookletLayout}, for example the
+ * {@link mw.ForeignStructuredUpload.BookletLayout}, like in the case of the upload
+ * interface in VisualEditor, you can pass it in through the `bookletClass` config option:
*
* var uploadDialog = new mw.Upload.Dialog( {
* bookletClass: mw.ForeignStructuredUpload.BookletLayout
@@ -26,15 +28,13 @@
*
*
* @class mw.Upload.Dialog
- * @uses mw.Upload
- * @uses mw.Upload.BookletLayout
* @extends OO.ui.ProcessDialog
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {Function} [bookletClass=mw.Upload.BookletLayout] Booklet class to be
+ * @param {Function} [config.bookletClass=mw.Upload.BookletLayout] Booklet class to be
* used for the steps
- * @cfg {Object} [booklet] Booklet constructor configuration
+ * @param {Object} [config.booklet] Booklet constructor configuration
*/
mw.Upload.Dialog = function ( config ) {
// Config initialization
@@ -108,6 +108,7 @@
/* Methods */
/**
+ * @ignore
* @inheritdoc
*/
mw.Upload.Dialog.prototype.initialize = function () {
@@ -125,7 +126,7 @@
};
/**
- * Create an upload booklet
+ * Create an upload booklet.
*
* @protected
* @return {mw.Upload.BookletLayout} An upload booklet
@@ -138,6 +139,7 @@
};
/**
+ * @ignore
* @inheritdoc
*/
mw.Upload.Dialog.prototype.getBodyHeight = function () {
@@ -145,7 +147,7 @@
};
/**
- * Handle panelNameSet events from the upload booklet
+ * Handle panelNameSet events from the upload booklet.
*
* @protected
* @param {OO.ui.PageLayout} page Current page
@@ -156,7 +158,7 @@
};
/**
- * Handle uploadValid events
+ * Handle uploadValid events.
*
* {@link OO.ui.ActionSet#setAbilities Sets abilities}
* for the dialog accordingly.
@@ -169,7 +171,7 @@
};
/**
- * Handle infoValid events
+ * Handle infoValid events.
*
* {@link OO.ui.ActionSet#setAbilities Sets abilities}
* for the dialog accordingly.
@@ -182,6 +184,7 @@
};
/**
+ * @ignore
* @inheritdoc
*/
mw.Upload.Dialog.prototype.getSetupProcess = function ( data ) {
@@ -192,6 +195,7 @@
};
/**
+ * @ignore
* @inheritdoc
*/
mw.Upload.Dialog.prototype.getActionProcess = function ( action ) {
@@ -219,6 +223,7 @@
};
/**
+ * @ignore
* @inheritdoc
*/
mw.Upload.Dialog.prototype.getTeardownProcess = function ( data ) {
diff --git a/resources/src/mediawiki.Upload.js b/resources/src/mediawiki.Upload.js
index f31a787bfc05..f94424b02079 100644
--- a/resources/src/mediawiki.Upload.js
+++ b/resources/src/mediawiki.Upload.js
@@ -9,39 +9,39 @@
* actions in a logical way.
*
* A simple example:
- *
- * var file = new OO.ui.SelectFileInputWidget(),
- * button = new OO.ui.ButtonWidget( { label: 'Save' } ),
- * upload = new mw.Upload;
- *
- * button.on( 'click', function () {
- * upload.setFile( file.getValue() );
- * upload.setFilename( file.getValue().name );
- * upload.upload();
- * } );
- *
- * $( document.body ).append( file.$element, button.$element );
- *
+ * ```
+ * var file = new OO.ui.SelectFileWidget(),
+ * button = new OO.ui.ButtonWidget( { label: 'Save' } ),
+ * upload = new mw.Upload;
+ *
+ * button.on( 'click', function () {
+ * upload.setFile( file.getValue() );
+ * upload.setFilename( file.getValue().name );
+ * upload.upload();
+ * } );
+ *
+ * $( document.body ).append( file.$element, button.$element );
+ * ```
* You can also choose to {@link #uploadToStash stash the upload} and
* {@link #finishStashUpload finalize} it later:
- *
- * var file, // Some file object
- * upload = new mw.Upload,
- * stashPromise = $.Deferred();
- *
- * upload.setFile( file );
- * upload.uploadToStash().then( function () {
- * stashPromise.resolve();
- * } );
- *
- * stashPromise.then( function () {
- * upload.setFilename( 'foo' );
- * upload.setText( 'bar' );
- * upload.finishStashUpload().then( function () {
- * console.log( 'Done!' );
- * } );
- * } );
- *
+ * ```
+ * var file, // Some file object
+ * upload = new mw.Upload,
+ * stashPromise = $.Deferred();
+ *
+ * upload.setFile( file );
+ * upload.uploadToStash().then( function () {
+ * stashPromise.resolve();
+ * } );
+ *
+ * stashPromise.then( function () {
+ * upload.setFilename( 'foo' );
+ * upload.setText( 'bar' );
+ * upload.finishStashUpload().then( function () {
+ * console.log( 'Done!' );
+ * } );
+ * } );
+ * ```
* @class mw.Upload
*
* @constructor
diff --git a/resources/src/mediawiki.Uri/Uri.js b/resources/src/mediawiki.Uri/Uri.js
index b46fda3efcde..b606a174a293 100644
--- a/resources/src/mediawiki.Uri/Uri.js
+++ b/resources/src/mediawiki.Uri/Uri.js
@@ -103,39 +103,39 @@
*
* You can modify the properties directly, then use the {@link mw.Uri#toString toString} method to extract the full URI
* string again. Example:
+ * ```
+ * var uri = new mw.Uri( 'http://example.com/mysite/mypage.php?quux=2' );
*
- * var uri = new mw.Uri( 'http://example.com/mysite/mypage.php?quux=2' );
+ * if ( uri.host == 'example.com' ) {
+ * uri.host = 'foo.example.com';
+ * uri.extend( { bar: 1 } );
*
- * if ( uri.host == 'example.com' ) {
- * uri.host = 'foo.example.com';
- * uri.extend( { bar: 1 } );
- *
- * $( 'a#id1' ).attr( 'href', uri );
- * // anchor with id 'id1' now links to http://foo.example.com/mysite/mypage.php?bar=1&quux=2
- *
- * $( 'a#id2' ).attr( 'href', uri.clone().extend( { bar: 3, pif: 'paf' } ) );
- * // anchor with id 'id2' now links to http://foo.example.com/mysite/mypage.php?bar=3&quux=2&pif=paf
- * }
+ * $( 'a#id1' ).attr( 'href', uri );
+ * // anchor with id 'id1' now links to http://foo.example.com/mysite/mypage.php?bar=1&quux=2
*
+ * $( 'a#id2' ).attr( 'href', uri.clone().extend( { bar: 3, pif: 'paf' } ) );
+ * // anchor with id 'id2' now links to http://foo.example.com/mysite/mypage.php?bar=3&quux=2&pif=paf
+ * }
+ * ```
* Given a URI like
* `http://usr:pwd@www.example.com:81/dir/dir.2/index.htm?q1=0&&test1&test2=&test3=value+%28escaped%29&r=1&r=2#top`
* the returned object will have the following properties:
- *
- * protocol 'http'
- * user 'usr'
- * password 'pwd'
- * host 'www.example.com'
- * port '81'
- * path '/dir/dir.2/index.htm'
- * query {
- * q1: '0',
- * test1: null,
- * test2: '',
- * test3: 'value (escaped)'
- * r: ['1', '2']
- * }
- * fragment 'top'
- *
+ * ```
+ * protocol 'http'
+ * user 'usr'
+ * password 'pwd'
+ * host 'www.example.com'
+ * port '81'
+ * path '/dir/dir.2/index.htm'
+ * query {
+ * q1: '0',
+ * test1: null,
+ * test2: '',
+ * test3: 'value (escaped)'
+ * r: ['1', '2']
+ * }
+ * fragment 'top'
+ * ```
* (N.b., 'password' is technically not allowed for HTTP URIs, but it is possible with other kinds
* of URIs.)
*
@@ -272,7 +272,7 @@
* compliant with RFC 3986. Similar to rawurlencode from PHP and our JS library
* {@link module:mediawiki.util.rawurlencode mw.util.rawurlencode}, except this also replaces spaces with `+`.
*
- * @function
+ * @method
* @name mw.Uri.encode
* @param {string} s String to encode
* @return {string} Encoded string for URI
@@ -290,7 +290,7 @@
* Reversed {@link mw.Uri.encode encode}. Standard decodeURIComponent, with addition of replacing
* `+` with a space.
*
- * @function
+ * @method
* @name mw.Uri.decode
* @param {string} s String to decode
* @return {string} Decoded string
diff --git a/resources/src/mediawiki.api/index.js b/resources/src/mediawiki.api/index.js
index b459024d792d..deddf65f09ba 100644
--- a/resources/src/mediawiki.api/index.js
+++ b/resources/src/mediawiki.api/index.js
@@ -23,23 +23,27 @@
* @classdesc Interact with the API of a particular MediaWiki site. mw.Api objects represent the API of
* one particular MediaWiki site.
*
- * var api = new mw.Api();
- * api.get( {
- * action: 'query',
- * meta: 'userinfo'
- * } ).done( function ( data ) {
- * console.log( data );
- * } );
+ * ```
+ * var api = new mw.Api();
+ * api.get( {
+ * action: 'query',
+ * meta: 'userinfo'
+ * } ).done( function ( data ) {
+ * console.log( data );
+ * } );
+ * ```
*
* Since MW 1.25, multiple values for a parameter can be specified using an array:
*
- * var api = new mw.Api();
- * api.get( {
- * action: 'query',
- * meta: [ 'userinfo', 'siteinfo' ] // same effect as 'userinfo|siteinfo'
- * } ).done( function ( data ) {
- * console.log( data );
- * } );
+ * ```
+ * var api = new mw.Api();
+ * api.get( {
+ * action: 'query',
+ * meta: [ 'userinfo', 'siteinfo' ] // same effect as 'userinfo|siteinfo'
+ * } ).done( function ( data ) {
+ * console.log( data );
+ * } );
+ * ```
*
* Since MW 1.26, boolean values for API parameters can be specified natively. Parameter
* values set to `false` or `undefined` will be omitted from the request, as required by
@@ -473,9 +477,11 @@
* For better quality of error messages, it's recommended to use the following options in your
* API queries:
*
- * errorformat: 'html',
- * errorlang: mw.config.get( 'wgUserLanguage' ),
- * errorsuselocal: true,
+ * ```
+ * errorformat: 'html',
+ * errorlang: mw.config.get( 'wgUserLanguage' ),
+ * errorsuselocal: true,
+ * ```
*
* Error messages, particularly for editing pages, may consist of multiple paragraphs of text.
* Your user interface should have enough space for that.
diff --git a/resources/src/mediawiki.base/mediawiki.base.js b/resources/src/mediawiki.base/mediawiki.base.js
index 56a4454c85a9..a4358ae6bbc3 100644
--- a/resources/src/mediawiki.base/mediawiki.base.js
+++ b/resources/src/mediawiki.base/mediawiki.base.js
@@ -60,7 +60,7 @@ function Message( map, key, parameters ) {
this.parameters = parameters || [];
}
-Message.prototype = {
+Message.prototype = /** @lends mw.Message.prototype */ {
/**
* Get parsed contents of the message.
*
@@ -94,7 +94,6 @@ Message.prototype = {
/**
* Add (does not replace) parameters for `$N` placeholder values.
*
- * @memberof mw.Message.prototype
* @param {Array} parameters
* @return {mw.Message}
* @chainable
@@ -150,7 +149,6 @@ Message.prototype = {
* If jqueryMsg is loaded, this transforms text and parses a subset of supported wikitext
* into HTML. Without jqueryMsg, it is equivalent to #escaped.
*
- * @memberof mw.Message.prototype
* @return {string} String form of parsed message
*/
parse: function () {
@@ -163,7 +161,6 @@ Message.prototype = {
* This substitutes parameters, but otherwise does not transform the
* message content.
*
- * @memberof mw.Message.prototype
* @return {string} String form of plain message
*/
plain: function () {
@@ -177,7 +174,6 @@ Message.prototype = {
* magic words such as `{{plural:}}`, `{{gender:}}`, and `{{int:}}`.
* Without jqueryMsg, it is equivalent to #plain.
*
- * @memberof mw.Message.prototype
* @return {string} String form of text message
*/
text: function () {
@@ -189,7 +185,6 @@ Message.prototype = {
*
* This is equivalent to the #text format, which is then HTML-escaped.
*
- * @memberof mw.Message.prototype
* @return {string} String form of html escaped message
*/
escaped: function () {
@@ -199,7 +194,6 @@ Message.prototype = {
/**
* Check if a message exists. Equivalent to {@link mw.Map#exists}.
*
- * @memberof mw.Message.prototype
* @return {boolean}
*/
exists: function () {
@@ -461,9 +455,10 @@ trackCallbacks.fire( mw.trackQueue );
* hooks accordingly to ensure everything still works as expected.
*
* Example usage:
- *
- * mw.hook( 'wikipage.content' ).add( fn ).remove( fn );
- * mw.hook( 'wikipage.content' ).fire( $content );
+ * ```
+ * mw.hook( 'wikipage.content' ).add( fn ).remove( fn );
+ * mw.hook( 'wikipage.content' ).fire( $content );
+ * ```
*
* Handlers can be added and fired for arbitrary event names at any time. The same
* event can be fired multiple times. The last run of an event is memorized
@@ -476,8 +471,10 @@ trackCallbacks.fire( mw.trackQueue );
* You can pass around the `add` and/or `fire` method to another piece of code
* without it having to know the event name (or `mw.hook` for that matter).
*
- * var h = mw.hook( 'bar.ready' );
- * new mw.Foo( .. ).fetch( { callback: h.fire } );
+ * ```
+ * var h = mw.hook( 'bar.ready' );
+ * new mw.Foo( .. ).fetch( { callback: h.fire } );
+ * ```
*
* See available global events below.
*/
diff --git a/resources/src/mediawiki.feedback/feedback.js b/resources/src/mediawiki.feedback/feedback.js
index 1a2a111f4415..9f3f2c06ca8d 100644
--- a/resources/src/mediawiki.feedback/feedback.js
+++ b/resources/src/mediawiki.feedback/feedback.js
@@ -26,7 +26,7 @@
* This feature works with any content model that defines a
* `mw.messagePoster.MessagePoster`.
*
- * @example Minimal usage example:
+ * @example // Minimal usage example
* mw.loader.using( 'mediawiki.feedback').then(() => {
* var feedback = new mw.Feedback();
* $( '#myButton' ).click( function () { feedback.launch(); } );
@@ -39,17 +39,16 @@
* ResourceLoader module.
* @constructor
* @param {Object} [config] Configuration object
- * @cfg {mw.Title} [title="Feedback"] The title of the page where you collect
+ * @param {mw.Title} [config.title="Feedback"] The title of the page where you collect
* feedback.
- * @cfg {string} [apiUrl] api.php URL if the feedback page is on another wiki
- * @cfg {string} [dialogTitleMessageKey="feedback-dialog-title"] Message key for the
+ * @param {string} [config.apiUrl] api.php URL if the feedback page is on another wiki
+ * @param {string} [config.dialogTitleMessageKey="feedback-dialog-title"] Message key for the
* title of the dialog box
- * @cfg {mw.Uri|string} [bugsLink="//phabricator.wikimedia.org/maniphest/task/edit/form/1/"] URL where
+ * @param {mw.Uri|string} [config.bugsLink="//phabricator.wikimedia.org/maniphest/task/edit/form/1/"] URL where
* bugs can be posted
- * @cfg {boolean} [showUseragentCheckbox=false] Show a Useragent agreement checkbox as part of the form.
- * @cfg {boolean} [useragentCheckboxMandatory=false] Make the Useragent checkbox mandatory.
- * @cfg {string|jQuery} [useragentCheckboxMessage] Supply a custom message for the useragent checkbox.
- * defaults to the message 'feedback-terms'.
+ * @param {boolean} [config.showUseragentCheckbox=false] Show a Useragent agreement checkbox as part of the form.
+ * @param {boolean} [config.useragentCheckboxMandatory=false] Make the Useragent checkbox mandatory.
+ * @param {string|jQuery} [config.useragentCheckboxMessage="feedback-terms"] Supply a custom message for the useragent checkbox.
*/
mw.Feedback = function MwFeedback( config ) {
config = config || {};
diff --git a/resources/src/mediawiki.htmlform.ooui/Element.js b/resources/src/mediawiki.htmlform.ooui/Element.js
index cfc85ee5a828..454586dc16ad 100644
--- a/resources/src/mediawiki.htmlform.ooui/Element.js
+++ b/resources/src/mediawiki.htmlform.ooui/Element.js
@@ -1,5 +1,10 @@
( function () {
+ /**
+ * Provides access to HTMLForm OOUI classes.
+ *
+ * @namespace mw.htmlform
+ */
mw.htmlform = {};
/**
@@ -9,8 +14,13 @@
*
* Currently only supports passing 'cond-state' data.
*
- * @ignore
+ * @classdesc HTMLForm Element class.
+ * @class
* @param {Object} [config] Configuration options
+ * @param {Object<string,string[]>} [config.condState] typically corresponds to a data-cond-state attribute
+ * that is found on HTMLForm elements and used during
+ * {@link Hooks~'htmlform.enhance' htmlform.enhance}. For more information on the format see
+ * the private function conditionParse in resources/src/mediawiki.htmlform/cond-state.js.
*/
mw.htmlform.Element = function ( config ) {
// Configuration initialization
@@ -20,6 +30,15 @@
this.condState = config.condState;
};
+ /**
+ * Create a FieldLayout class.
+ *
+ * @class
+ * @extends {OO.ui.FieldLayout}
+ * @classdesc FieldLayout class. Mixes in HTMLForm Element class.
+ * @memberof mw.htmlform
+ * @param {Object} config
+ */
mw.htmlform.FieldLayout = function ( config ) {
// Parent constructor
mw.htmlform.FieldLayout.super.call( this, config );
@@ -29,6 +48,15 @@
OO.inheritClass( mw.htmlform.FieldLayout, OO.ui.FieldLayout );
OO.mixinClass( mw.htmlform.FieldLayout, mw.htmlform.Element );
+ /**
+ * Create an ActionFieldLayout class.
+ *
+ * @class
+ * @extends {OO.ui.ActionFieldLayout}
+ * @classdesc FieldLayout class. Mixes in HTMLForm Element class.
+ * @memberof mw.htmlform
+ * @param {Object} config
+ */
mw.htmlform.ActionFieldLayout = function ( config ) {
// Parent constructor
mw.htmlform.ActionFieldLayout.super.call( this, config );
diff --git a/resources/src/mediawiki.htmlform/htmlform.js b/resources/src/mediawiki.htmlform/htmlform.js
index 35b1d76bb81f..65bbdc0fc207 100644
--- a/resources/src/mediawiki.htmlform/htmlform.js
+++ b/resources/src/mediawiki.htmlform/htmlform.js
@@ -1,9 +1,15 @@
( function () {
$( function () {
+ /**
+ * Fired on page load to enhance any HTML forms on the page.
+ *
+ * @event ~'htmlform.enhance'
+ * @param {jQuery} document
+ * @memberof Hooks
+ */
mw.hook( 'htmlform.enhance' ).fire( $( document ) );
} );
-
mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
// Turn HTML5 form validation back on, in cases where it was disabled server-side (see
// HTMLForm::needsJSForHtml5FormValidation()) because we need extra logic implemented in JS to
diff --git a/resources/src/mediawiki.htmlform/selectorother.js b/resources/src/mediawiki.htmlform/selectorother.js
index 76c9f3e17253..068836e6d607 100644
--- a/resources/src/mediawiki.htmlform/selectorother.js
+++ b/resources/src/mediawiki.htmlform/selectorother.js
@@ -5,15 +5,12 @@
( function () {
/**
- * @class jQuery.plugin.htmlform
- */
-
- /**
* jQuery plugin to fade or snap to visible state.
*
+ * @memberof jQueryPlugins
+ * @method goIn
* @param {boolean} [instantToggle=false]
* @return {jQuery}
- * @chainable
*/
$.fn.goIn = function ( instantToggle ) {
if ( instantToggle === true ) {
@@ -25,9 +22,10 @@
/**
* jQuery plugin to fade or snap to hiding state.
*
+ * @memberof jQueryPlugins
+ * @method goOut
* @param {boolean} [instantToggle=false]
* @return {jQuery}
- * @chainable
*/
$.fn.goOut = function ( instantToggle ) {
if ( instantToggle === true ) {
diff --git a/resources/src/mediawiki.jqueryMsg/mediawiki.jqueryMsg.js b/resources/src/mediawiki.jqueryMsg/mediawiki.jqueryMsg.js
index 27dcfb43403a..de2223a9aa1e 100644
--- a/resources/src/mediawiki.jqueryMsg/mediawiki.jqueryMsg.js
+++ b/resources/src/mediawiki.jqueryMsg/mediawiki.jqueryMsg.js
@@ -1450,7 +1450,7 @@ HtmlEmitter.prototype = {
* @name msg
* @param {string} message key
* @param {...string[]} arguments
- * @function
+ * @method
* @example
* mw.loader.using('mediawiki.jqueryMsg' ).then(() => {
* var $userlink = $( '<a>' ).click( function () { alert( "hello!!" ) } );
diff --git a/resources/src/mediawiki.page.ready/checkboxHack.js b/resources/src/mediawiki.page.ready/checkboxHack.js
index 85169b14040d..4519ef944bc3 100644
--- a/resources/src/mediawiki.page.ready/checkboxHack.js
+++ b/resources/src/mediawiki.page.ready/checkboxHack.js
@@ -119,11 +119,12 @@
* [0]: https://developer.mozilla.org/docs/Web/HTML/Element/details
*
* @namespace CheckboxHack
+ * @memberof module:mediawiki.page.ready
*/
/**
* Revise the button's `aria-expanded` state to match the checked state.
*
- * @memberof CheckboxHack
+ * @memberof module:mediawiki.page.ready.CheckboxHack
* @param {HTMLInputElement} checkbox
* @param {HTMLElement} button
*/
@@ -216,7 +217,7 @@ function dismissIfExternalEventTarget( checkbox, button, target, event ) {
/**
* Update the `aria-expanded` attribute based on checkbox state (target visibility) changes.
*
- * @memberof CheckboxHack
+ * @memberof module:mediawiki.page.ready.CheckboxHack
* @param {HTMLInputElement} checkbox
* @param {HTMLElement} button
* @return {function(): void} Cleanup function that removes the added event listeners.
@@ -238,7 +239,7 @@ function bindUpdateAriaExpandedOnInput( checkbox, button ) {
/**
* Manually change the checkbox state to avoid a focus change when using a pointing device.
*
- * @memberof CheckboxHack
+ * @memberof module:mediawiki.page.ready.CheckboxHack
* @param {HTMLInputElement} checkbox
* @param {HTMLElement} button
* @return {function(): void} Cleanup function that removes the added event listeners.
@@ -261,7 +262,7 @@ function bindToggleOnClick( checkbox, button ) {
* Manually change the checkbox state when the button is focused and SPACE is pressed.
*
* @deprecated Use `bindToggleOnEnter` instead.
- * @memberof CheckboxHack
+ * @memberof module:mediawiki.page.ready.CheckboxHack
* @param {HTMLInputElement} checkbox
* @param {HTMLElement} button
* @return {function(): void} Cleanup function that removes the added event listeners.
@@ -311,7 +312,7 @@ function bindToggleOnSpaceEnter( checkbox, button ) {
/**
* Manually change the checkbox state when the button is focused and Enter is pressed.
*
- * @memberof CheckboxHack
+ * @memberof module:mediawiki.page.ready.CheckboxHack
* @param {HTMLInputElement} checkbox
* @return {function(): void} Cleanup function that removes the added event listeners.
*/
@@ -336,7 +337,7 @@ function bindToggleOnEnter( checkbox ) {
* Dismiss the target when clicking elsewhere and update the `aria-expanded` attribute based on
* checkbox state (target visibility).
*
- * @memberof CheckboxHack
+ * @memberof module:mediawiki.page.ready.CheckboxHack
* @param {window} window
* @param {HTMLInputElement} checkbox
* @param {HTMLElement} button
@@ -361,7 +362,7 @@ function bindDismissOnClickOutside( window, checkbox, button, target ) {
* @param {HTMLElement} button
* @param {Node} target
* @return {function(): void} Cleanup function that removes the added event listeners.
- * @memberof CheckboxHack
+ * @memberof module:mediawiki.page.ready.CheckboxHack
*/
function bindDismissOnFocusLoss( window, checkbox, button, target ) {
// If focus is given to any element outside the target, dismiss the target. Setting a focusout
@@ -381,12 +382,14 @@ function bindDismissOnFocusLoss( window, checkbox, button, target ) {
* @param {HTMLInputElement} checkbox
* @param {Node} target
* @return {function(): void} Cleanup function that removes the added event listeners.
- * @memberof CheckboxHack
+ * @memberof module:mediawiki.page.ready.CheckboxHack
*/
function bindDismissOnClickLink( checkbox, target ) {
function dismissIfClickLinkEvent( event ) {
// Handle clicks to links and link children elements
- if ( event.target &&
+ if (
+ // check that the element wasn't removed from the DOM.
+ event.target && event.target.parentNode &&
( event.target.nodeName === 'A' || event.target.parentNode.nodeName === 'A' )
) {
setCheckedState( checkbox, false );
@@ -407,7 +410,7 @@ function bindDismissOnClickLink( checkbox, target ) {
* This function calls the other bind* functions and is the only expected interaction for most use
* cases. It's constituents are provided distinctly for the other use cases.
*
- * @memberof CheckboxHack
+ * @memberof module:mediawiki.page.ready.CheckboxHack
* @param {window} window
* @param {HTMLInputElement} checkbox The underlying hidden checkbox that controls target
* visibility.
diff --git a/resources/src/mediawiki.page.ready/ready.js b/resources/src/mediawiki.page.ready/ready.js
index 6684928a9618..f91ee1adf9ff 100644
--- a/resources/src/mediawiki.page.ready/ready.js
+++ b/resources/src/mediawiki.page.ready/ready.js
@@ -294,7 +294,7 @@ try {
*/
module.exports = {
loadSearchModule,
- /** @type {CheckboxHack} */
+ /** @type {module:mediawiki.page.ready.CheckboxHack} */
checkboxHack: require( './checkboxHack.js' ),
/**
* A container for displaying elements that overlay the page, such as dialogs.
diff --git a/resources/src/mediawiki.page.ready/toggleAllCollapsibles.js b/resources/src/mediawiki.page.ready/toggleAllCollapsibles.js
index dc0d2c05c990..be76264b62f4 100644
--- a/resources/src/mediawiki.page.ready/toggleAllCollapsibles.js
+++ b/resources/src/mediawiki.page.ready/toggleAllCollapsibles.js
@@ -2,46 +2,33 @@
* Add portlet link to toggle all collapsibles created by
* the jquery.makeCollapsible module.
*/
-// @param {jQuery} $collapsible an element that has been made collapsible
-const addCollapsibleAll = function ( $collapsible ) {
+let toggleAll;
- // get toolbox and page content
- const toolboxId = 'p-tb',
- toolbox = document.getElementById( toolboxId );
-
- // return early if there isn't a toolbox
- if ( !toolbox ) {
+mw.hook( 'wikipage.content' ).add( function () {
+ // return early if the link was already added
+ if ( toggleAll ) {
return;
}
// return early if there are no collapsibles within the parsed page content
- if ( $collapsible.closest( '#mw-content-text .mw-parser-output' ).length === 0 ) {
- return;
- }
-
- const portletId = 't-collapsible-toggle-all';
-
- // return early if the link was already added
- let portletLink = document.getElementById( portletId );
- if ( portletLink ) {
+ if ( !document.querySelector( '#mw-content-text .mw-parser-output .mw-collapsible' ) ) {
return;
}
// create portlet link for expand/collapse all
- portletLink = mw.util.addPortletLink(
- toolboxId,
+ const portletLink = mw.util.addPortletLink(
+ 'p-tb',
'#',
mw.msg( 'collapsible-expand-all-text' ),
- portletId,
+ 't-collapsible-toggle-all',
mw.msg( 'collapsible-expand-all-tooltip' )
);
-
- // return early if no link was added
+ // return early if no link was added (e.g. no toolbox)
if ( !portletLink ) {
return;
}
// set up the toggle link
- const toggleAll = portletLink.querySelector( 'a' );
+ toggleAll = portletLink.querySelector( 'a' );
toggleAll.setAttribute( 'role', 'button' );
// initially treat as collapsed
@@ -56,9 +43,9 @@ const addCollapsibleAll = function ( $collapsible ) {
if ( !allExpanded ) {
const collapsed = document.querySelectorAll( '#mw-content-text .mw-parser-output .mw-made-collapsible.mw-collapsed' );
Array.prototype.forEach.call( collapsed, function ( collapsible ) {
- $( collapsible ).data( 'mwCollapsible' ).expand();
+ $( collapsible ).data( 'mw-collapsible' ).expand();
} );
- toggleAll.innerText = mw.msg( 'collapsible-collapse-all-text' );
+ toggleAll.textContent = mw.msg( 'collapsible-collapse-all-text' );
toggleAll.title = mw.msg( 'collapsible-collapse-all-tooltip' );
toggleAll.setAttribute( 'aria-expanded', 'true' );
allExpanded = true;
@@ -66,17 +53,12 @@ const addCollapsibleAll = function ( $collapsible ) {
} else {
const expanded = document.querySelectorAll( '#mw-content-text .mw-parser-output .mw-made-collapsible:not( .mw-collapsed )' );
Array.prototype.forEach.call( expanded, function ( collapsible ) {
- $( collapsible ).data( 'mwCollapsible' ).collapse();
+ $( collapsible ).data( 'mw-collapsible' ).collapse();
} );
- toggleAll.innerText = mw.msg( 'collapsible-expand-all-text' );
+ toggleAll.textContent = mw.msg( 'collapsible-expand-all-text' );
toggleAll.title = mw.msg( 'collapsible-expand-all-tooltip' );
toggleAll.setAttribute( 'aria-expanded', 'false' );
allExpanded = false;
}
} );
- // only run once
- mw.hook( 'wikipage.collapsibleContent' ).remove( addCollapsibleAll );
-};
-
-// add listener to collapsible content
-mw.hook( 'wikipage.collapsibleContent' ).add( addCollapsibleAll );
+} );
diff --git a/resources/src/mediawiki.rcfilters/Controller.js b/resources/src/mediawiki.rcfilters/Controller.js
index 9e3de3df147b..cdfb6e3dc8fe 100644
--- a/resources/src/mediawiki.rcfilters/Controller.js
+++ b/resources/src/mediawiki.rcfilters/Controller.js
@@ -4,21 +4,21 @@ var byteLength = require( 'mediawiki.String' ).byteLength,
/* eslint no-underscore-dangle: "off" */
/**
- * Controller for the filters in Recent Changes
+ * Controller for the filters in Recent Changes.
*
- * @class mw.rcfilters.Controller
- *
- * @constructor
+ * @class Controller
+ * @memberof mw.rcfilters
+ * @ignore
* @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Filters view model
* @param {mw.rcfilters.dm.ChangesListViewModel} changesListModel Changes list view model
* @param {mw.rcfilters.dm.SavedQueriesModel} savedQueriesModel Saved queries model
* @param {Object} config Additional configuration
- * @cfg {string} savedQueriesPreferenceName Where to save the saved queries
- * @cfg {string} daysPreferenceName Preference name for the days filter
- * @cfg {string} limitPreferenceName Preference name for the limit filter
- * @cfg {string} collapsedPreferenceName Preference name for collapsing and showing
+ * @param {string} config.savedQueriesPreferenceName Where to save the saved queries
+ * @param {string} config.daysPreferenceName Preference name for the days filter
+ * @param {string} config.limitPreferenceName Preference name for the limit filter
+ * @param {string} config.collapsedPreferenceName Preference name for collapsing and showing
* the active filters area
- * @cfg {boolean} [normalizeTarget] Dictates whether or not to go through the
+ * @param {boolean} [config.normalizeTarget] Dictates whether or not to go through the
* title normalization to separate title subpage/parts into the target= url
* parameter
*/
@@ -558,6 +558,12 @@ Controller.prototype.toggleHighlight = function () {
this.uriProcessor.updateURL();
if ( this.filtersModel.isHighlightEnabled() ) {
+ /**
+ * Fires when highlight feature is enabled.
+ *
+ * @event ~'RcFilters.highlight.enable'
+ * @memberof Hooks
+ */
mw.hook( 'RcFilters.highlight.enable' ).fire();
}
};
@@ -848,6 +854,7 @@ Controller.prototype.applySavedQuery = function ( queryID ) {
* Check whether the current filter and highlight state exists
* in the saved queries model.
*
+ * @ignore
* @return {mw.rcfilters.dm.SavedQueryItemModel} Matching item model
*/
Controller.prototype.findQueryMatchingCurrentState = function () {
diff --git a/resources/src/mediawiki.rcfilters/HighlightColors.js b/resources/src/mediawiki.rcfilters/HighlightColors.js
index 42bfae6a366e..27939db64d4a 100644
--- a/resources/src/mediawiki.rcfilters/HighlightColors.js
+++ b/resources/src/mediawiki.rcfilters/HighlightColors.js
@@ -2,7 +2,7 @@
* Supported highlight colors.
* Warning: These are also hardcoded in "styles/mw.rcfilters.variables.less"
*
- * @member mw.rcfilters
+ * @memberof mw.rcfilters
* @property {string[]}
*/
var HighlightColors = [ 'c1', 'c2', 'c3', 'c4', 'c5' ];
diff --git a/resources/src/mediawiki.rcfilters/UriProcessor.js b/resources/src/mediawiki.rcfilters/UriProcessor.js
index 840d2fa07727..4c42878232d7 100644
--- a/resources/src/mediawiki.rcfilters/UriProcessor.js
+++ b/resources/src/mediawiki.rcfilters/UriProcessor.js
@@ -1,13 +1,13 @@
/* eslint no-underscore-dangle: "off" */
/**
- * URI Processor for RCFilters
+ * URI Processor for RCFilters.
*
- * @class mw.rcfilters.UriProcessor
- *
- * @constructor
+ * @class UriProcessor
+ * @memberof mw.rcfilters
+ * @ignore
* @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Filters view model
* @param {Object} [config] Configuration object
- * @cfg {boolean} [normalizeTarget] Dictates whether or not to go through the
+ * @param {boolean} [config.normalizeTarget] Dictates whether or not to go through the
* title normalization to separate title subpage/parts into the target= url
* parameter
*/
diff --git a/resources/src/mediawiki.rcfilters/dm/ChangesListViewModel.js b/resources/src/mediawiki.rcfilters/dm/ChangesListViewModel.js
index d5357e09607c..bdd0dfae8ff6 100644
--- a/resources/src/mediawiki.rcfilters/dm/ChangesListViewModel.js
+++ b/resources/src/mediawiki.rcfilters/dm/ChangesListViewModel.js
@@ -1,11 +1,11 @@
/**
- * View model for the changes list
+ * View model for the changes list.
*
* @class mw.rcfilters.dm.ChangesListViewModel
+ * @ignore
* @mixins OO.EventEmitter
*
* @param {jQuery} $initialFieldset The initial server-generated legacy form content
- * @constructor
*/
var ChangesListViewModel = function MwRcfiltersDmChangesListViewModel( $initialFieldset ) {
// Mixin constructor
@@ -26,34 +26,38 @@ OO.mixinClass( ChangesListViewModel, OO.EventEmitter );
/* Events */
/**
- * @event invalidate
+ * The list of changes is now invalid (out of date).
*
- * The list of changes is now invalid (out of date)
+ * @event invalidate
+ * @ignore
*/
/**
+ * The list of changes has been updated.
+ *
* @event update
+ * @ignore
* @param {jQuery|string} $changesListContent List of changes
* @param {jQuery} $fieldset Server-generated form
* @param {string} noResultsDetails Type of no result error
* @param {boolean} isInitialDOM Whether the previous dom variables are from the initial page load
* @param {boolean} fromLiveUpdate These are new changes fetched via Live Update
- *
- * The list of changes has been updated
*/
/**
+ * The existence of changes newer than those currently displayed has changed.
+ *
* @event newChangesExist
* @param {boolean} newChangesExist
- *
- * The existence of changes newer than those currently displayed has changed.
+ * @ignore
*/
/**
+ * The state of the 'live update' feature has changed.
+ *
* @event liveUpdateChange
* @param {boolean} enable
- *
- * The state of the 'live update' feature has changed.
+ * @ignore
*/
/* Methods */
@@ -62,6 +66,7 @@ OO.mixinClass( ChangesListViewModel, OO.EventEmitter );
* Invalidate the list of changes
*
* @fires invalidate
+ * @ignore
*/
ChangesListViewModel.prototype.invalidate = function () {
if ( this.valid ) {
diff --git a/resources/src/mediawiki.rcfilters/dm/FilterGroup.js b/resources/src/mediawiki.rcfilters/dm/FilterGroup.js
index b0473c1f6874..e59b40cc9285 100644
--- a/resources/src/mediawiki.rcfilters/dm/FilterGroup.js
+++ b/resources/src/mediawiki.rcfilters/dm/FilterGroup.js
@@ -3,44 +3,44 @@ var FilterItem = require( './FilterItem.js' ),
FilterGroup;
/**
- * View model for a filter group
+ * View model for a filter group.
*
* @class mw.rcfilters.dm.FilterGroup
+ * @ignore
* @mixins OO.EventEmitter
* @mixins OO.EmitterList
*
- * @constructor
* @param {string} name Group name
* @param {Object} [config] Configuration options
- * @cfg {string} [type='send_unselected_if_any'] Group type
- * @cfg {string} [view='default'] Name of the display group this group
+ * @param {string} [config.type='send_unselected_if_any'] Group type
+ * @param {string} [config.view='default'] Name of the display group this group
* is a part of.
- * @cfg {boolean} [sticky] This group is 'sticky'. It is synchronized
+ * @param {boolean} [config.sticky] This group is 'sticky'. It is synchronized
* with a preference, does not participate in Saved Queries, and is
* not shown in the active filters area.
- * @cfg {string} [title] Group title
- * @cfg {boolean} [hidden] This group is hidden from the regular menu views
+ * @param {string} [config.title] Group title
+ * @param {boolean} [config.hidden] This group is hidden from the regular menu views
* and the active filters area.
- * @cfg {boolean} [allowArbitrary] Allows for an arbitrary value to be added to the
+ * @param {boolean} [config.allowArbitrary] Allows for an arbitrary value to be added to the
* group from the URL, even if it wasn't initially set up.
- * @cfg {number} [range] An object defining minimum and maximum values for numeric
+ * @param {number} [config.range] An object defining minimum and maximum values for numeric
* groups. { min: x, max: y }
- * @cfg {number} [minValue] Minimum value for numeric groups
- * @cfg {string} [separator='|'] Value separator for 'string_options' groups
- * @cfg {boolean} [supportsAll=true] For 'string_options' groups, whether the magic 'all' value
+ * @param {number} [config.minValue] Minimum value for numeric groups
+ * @param {string} [config.separator='|'] Value separator for 'string_options' groups
+ * @param {boolean} [supportsAll=true] For 'string_options' groups, whether the magic 'all' value
* is understood to mean all options are selected.
- * @cfg {boolean} [active] Group is active
- * @cfg {boolean} [fullCoverage] This filters in this group collectively cover all results
- * @cfg {Object} [conflicts] Defines the conflicts for this filter group
- * @cfg {string|Object} [labelPrefixKey] An i18n key defining the prefix label for this
+ * @param {boolean} [config.active] Group is active
+ * @param {boolean} [config.fullCoverage] This filters in this group collectively cover all results
+ * @param {Object} [config.conflicts] Defines the conflicts for this filter group
+ * @param {string|Object} [config.labelPrefixKey] An i18n key defining the prefix label for this
* group. If the prefix has 'invert' state, the parameter is expected to be an object
* with 'default' and 'inverted' as keys.
- * @cfg {Object} [whatsThis] Defines the messages that should appear for the 'what's this' popup
- * @cfg {string} [whatsThis.header] The header of the whatsThis popup message
- * @cfg {string} [whatsThis.body] The body of the whatsThis popup message
- * @cfg {string} [whatsThis.url] The url for the link in the whatsThis popup message
- * @cfg {string} [whatsThis.linkMessage] The text for the link in the whatsThis popup message
- * @cfg {boolean} [visible=true] The visibility of the group
+ * @param {Object} [config.whatsThis] Defines the messages that should appear for the 'what's this' popup
+ * @param {string} [config.whatsThis.header] The header of the whatsThis popup message
+ * @param {string} [config.whatsThis.body] The body of the whatsThis popup message
+ * @param {string} [config.whatsThis.url] The url for the link in the whatsThis popup message
+ * @param {string} [config.whatsThis.linkMessage] The text for the link in the whatsThis popup message
+ * @param {boolean} [config.visible=true] The visibility of the group
*/
FilterGroup = function MwRcfiltersDmFilterGroup( name, config ) {
config = config || {};
@@ -84,9 +84,10 @@ OO.mixinClass( FilterGroup, OO.EmitterList );
/* Events */
/**
- * @event update
+ * Group state has been updated.
*
- * Group state has been updated
+ * @event update
+ * @ignore
*/
/* Methods */
@@ -480,6 +481,7 @@ FilterGroup.prototype.areAllSelected = function () {
/**
* Get all selected items in this group
*
+ * @ignore
* @param {mw.rcfilters.dm.FilterItem} [excludeItem] Item to exclude from the list
* @return {mw.rcfilters.dm.FilterItem[]} Selected items
*/
@@ -764,7 +766,7 @@ FilterGroup.prototype.getFilterRepresentation = function ( paramRepresentation )
};
/**
- * @return {*} The appropriate falsy value for this group type
+ * @return {any} The appropriate falsy value for this group type
*/
FilterGroup.prototype.getFalsyValue = function () {
return this.getType() === 'any_value' ? '' : false;
@@ -788,6 +790,7 @@ FilterGroup.prototype.getSelectedState = function () {
/**
* Get item by its filter name
*
+ * @ignore
* @param {string} filterName Filter name
* @return {mw.rcfilters.dm.FilterItem} Filter item
*/
@@ -811,6 +814,7 @@ FilterGroup.prototype.selectItemByParamName = function ( paramName ) {
/**
* Get item by its parameter name
*
+ * @ignore
* @param {string} paramName Parameter name
* @return {mw.rcfilters.dm.FilterItem} Filter item
*/
diff --git a/resources/src/mediawiki.rcfilters/dm/FilterItem.js b/resources/src/mediawiki.rcfilters/dm/FilterItem.js
index 2de4bbc0e18d..5bfb1c6d4e29 100644
--- a/resources/src/mediawiki.rcfilters/dm/FilterItem.js
+++ b/resources/src/mediawiki.rcfilters/dm/FilterItem.js
@@ -2,20 +2,20 @@ var ItemModel = require( './ItemModel.js' ),
FilterItem;
/**
- * Filter item model
+ * Filter item model.
*
* @class mw.rcfilters.dm.FilterItem
+ * @ignore
* @extends mw.rcfilters.dm.ItemModel
*
- * @constructor
* @param {string} param Filter param name
* @param {mw.rcfilters.dm.FilterGroup} groupModel Filter group model
* @param {Object} config Configuration object
- * @cfg {string[]} [excludes=[]] A list of filter names this filter, if
+ * @param {string[]} [config.excludes=[]] A list of filter names this filter, if
* selected, makes inactive.
- * @cfg {string[]} [subset] Defining the names of filters that are a subset of this filter
- * @cfg {Object} [conflicts] Defines the conflicts for this filter
- * @cfg {boolean} [visible=true] The visibility of the group
+ * @param {string[]} [config.subset] Defining the names of filters that are a subset of this filter
+ * @param {Object} [config.conflicts] Defines the conflicts for this filter
+ * @param {boolean} [config.visible=true] The visibility of the group
*/
FilterItem = function MwRcfiltersDmFilterItem( param, groupModel, config ) {
config = config || {};
@@ -181,6 +181,7 @@ FilterItem.prototype.getStateMessage = function () {
/**
* Get the model of the group this filter belongs to
*
+ * @ignore
* @return {mw.rcfilters.dm.FilterGroup} Filter group model
*/
FilterItem.prototype.getGroupModel = function () {
diff --git a/resources/src/mediawiki.rcfilters/dm/FiltersViewModel.js b/resources/src/mediawiki.rcfilters/dm/FiltersViewModel.js
index 9715e5c8a334..807d922aaffe 100644
--- a/resources/src/mediawiki.rcfilters/dm/FiltersViewModel.js
+++ b/resources/src/mediawiki.rcfilters/dm/FiltersViewModel.js
@@ -4,13 +4,13 @@ var FilterGroup = require( './FilterGroup.js' ),
FiltersViewModel;
/**
- * View model for the filters selection and display
+ * View model for the filters selection and display.
*
* @class mw.rcfilters.dm.FiltersViewModel
+ * @ignore
* @mixins OO.EventEmitter
* @mixins OO.EmitterList
*
- * @constructor
*/
FiltersViewModel = function MwRcfiltersDmFiltersViewModel() {
// Mixin constructor
@@ -40,29 +40,33 @@ OO.mixinClass( FiltersViewModel, OO.EmitterList );
/* Events */
/**
- * @event initialize
+ * Filter list is initialized.
*
- * Filter list is initialized
+ * @event initialize
+ * @ignore
*/
/**
- * @event update
+ * Model has been updated.
*
- * Model has been updated
+ * @event update
+ * @ignore
*/
/**
+ * Filter item has changed.
+ *
* @event itemUpdate
* @param {mw.rcfilters.dm.FilterItem} item Filter item updated
- *
- * Filter item has changed
+ * @ignore
*/
/**
+ * Highlight feature has been toggled enabled or disabled.
+ *
* @event highlightChange
* @param {boolean} Highlight feature is enabled
- *
- * Highlight feature has been toggled enabled or disabled
+ * @ignore
*/
/* Methods */
@@ -186,6 +190,7 @@ FiltersViewModel.prototype.hasConflict = function () {
/**
* Get the first item with a current conflict
*
+ * @ignore
* @return {mw.rcfilters.dm.FilterItem|undefined} Conflicted item or undefined when not found
*/
FiltersViewModel.prototype.getFirstConflictedItem = function () {
@@ -594,6 +599,7 @@ FiltersViewModel.prototype.getFilterGroupsByView = function ( view ) {
/**
* Get an array of filters matching the given display group.
*
+ * @ignore
* @param {string} [view] Requested view. If not given, uses current view
* @return {mw.rcfilters.dm.FilterItem} Filter items matching the group
*/
@@ -956,6 +962,7 @@ FiltersViewModel.prototype.areTagsEffectivelyInverted = function () {
/**
* Get the item that matches the given name
*
+ * @ignore
* @param {string} name Filter name
* @return {mw.rcfilters.dm.FilterItem} Filter item
*/
@@ -1005,6 +1012,7 @@ FiltersViewModel.prototype.toggleFiltersSelected = function ( filterDef ) {
/**
* Get a group model from its name
*
+ * @ignore
* @param {string} groupName Group name
* @return {mw.rcfilters.dm.FilterGroup} Group model
*/
@@ -1015,6 +1023,7 @@ FiltersViewModel.prototype.getGroup = function ( groupName ) {
/**
* Get all filters within a specified group by its name
*
+ * @ignore
* @param {string} groupName Group name
* @return {mw.rcfilters.dm.FilterItem[]} Filters belonging to this group
*/
@@ -1099,6 +1108,7 @@ FiltersViewModel.prototype.findMatches = function ( query, returnFlat ) {
/**
* Get items that are highlighted
*
+ * @ignore
* @return {mw.rcfilters.dm.FilterItem[]} Highlighted items
*/
FiltersViewModel.prototype.getHighlightedItems = function () {
@@ -1111,6 +1121,7 @@ FiltersViewModel.prototype.getHighlightedItems = function () {
/**
* Get items that allow highlights even if they're not currently highlighted
*
+ * @ignore
* @return {mw.rcfilters.dm.FilterItem[]} Items supporting highlights
*/
FiltersViewModel.prototype.getItemsSupportingHighlights = function () {
@@ -1122,6 +1133,7 @@ FiltersViewModel.prototype.getItemsSupportingHighlights = function () {
/**
* Get all selected items
*
+ * @ignore
* @return {mw.rcfilters.dm.FilterItem[]} Selected items
*/
FiltersViewModel.prototype.findSelectedItems = function () {
@@ -1300,6 +1312,7 @@ FiltersViewModel.prototype.toggleInvertedNamespaces = function ( enable ) {
/**
* Get the model object that represents the 'invert' filter
*
+ * @ignore
* @param {string} view
* @return {mw.rcfilters.dm.FilterItem|null}
*/
@@ -1317,6 +1330,7 @@ FiltersViewModel.prototype.getInvertModel = function ( view ) {
/**
* Get the model object that represents the 'invert' filter
*
+ * @ignore
* @return {mw.rcfilters.dm.FilterItem}
*/
FiltersViewModel.prototype.getNamespacesInvertModel = function () {
@@ -1326,6 +1340,7 @@ FiltersViewModel.prototype.getNamespacesInvertModel = function () {
/**
* Get the model object that represents the 'invert' filter
*
+ * @ignore
* @return {mw.rcfilters.dm.FilterItem}
*/
FiltersViewModel.prototype.getTagsInvertModel = function () {
diff --git a/resources/src/mediawiki.rcfilters/dm/ItemModel.js b/resources/src/mediawiki.rcfilters/dm/ItemModel.js
index eccc5df997e5..6025020012aa 100644
--- a/resources/src/mediawiki.rcfilters/dm/ItemModel.js
+++ b/resources/src/mediawiki.rcfilters/dm/ItemModel.js
@@ -1,26 +1,26 @@
/**
- * RCFilter base item model
+ * RCFilter base item model.
*
* @class mw.rcfilters.dm.ItemModel
+ * @ignore
* @mixins OO.EventEmitter
*
- * @constructor
* @param {string} param Filter param name
* @param {Object} config Configuration object
- * @cfg {string} [label] The label for the filter
- * @cfg {string} [description] The description of the filter
- * @cfg {string|Object} [labelPrefixKey] An i18n key defining the prefix label for this
+ * @param {string} [config.label] The label for the filter
+ * @param {string} [config.description] The description of the filter
+ * @param {string|Object} [config.labelPrefixKey] An i18n key defining the prefix label for this
* group. If the prefix has 'invert' state, the parameter is expected to be an object
* with 'default' and 'inverted' as keys.
- * @cfg {boolean} [active=true] The filter is active and affecting the result
- * @cfg {boolean} [selected] The item is selected
- * @cfg {*} [value] The value of this item
- * @cfg {string} [namePrefix='item_'] A prefix to add to the param name to act as a unique
+ * @param {boolean} [config.active=true] The filter is active and affecting the result
+ * @param {boolean} [config.selected] The item is selected
+ * @param {any} [config.value] The value of this item
+ * @param {string} [config.namePrefix='item_'] A prefix to add to the param name to act as a unique
* identifier
- * @cfg {string} [cssClass] The class identifying the results that match this filter
- * @cfg {string[]} [identifiers] An array of identifiers for this item. They will be
+ * @param {string} [config.cssClass] The class identifying the results that match this filter
+ * @param {string[]} [config.identifiers] An array of identifiers for this item. They will be
* added and considered in the view.
- * @cfg {string} [defaultHighlightColor=null] If set, highlight this filter by default with this color
+ * @param {string} [config.defaultHighlightColor=null] If set, highlight this filter by default with this color
*/
var ItemModel = function MwRcfiltersDmItemModel( param, config ) {
config = config || {};
@@ -52,9 +52,10 @@ OO.mixinClass( ItemModel, OO.EventEmitter );
/* Events */
/**
- * @event update
+ * The state of this filter has changed.
*
- * The state of this filter has changed
+ * @event update
+ * @ignore
*/
/* Methods */
@@ -169,7 +170,7 @@ ItemModel.prototype.toggleSelected = function ( isSelected ) {
/**
* Get the value
*
- * @return {*}
+ * @return {any}
*/
ItemModel.prototype.getValue = function () {
return this.value;
@@ -178,8 +179,8 @@ ItemModel.prototype.getValue = function () {
/**
* Convert a given value to the appropriate representation based on group type
*
- * @param {*} value
- * @return {*}
+ * @param {any} value
+ * @return {any}
*/
ItemModel.prototype.coerceValue = function ( value ) {
return this.getGroupModel().getType() === 'any_value' ? value : !!value;
@@ -188,7 +189,7 @@ ItemModel.prototype.coerceValue = function ( value ) {
/**
* Set the value
*
- * @param {*} newValue
+ * @param {any} newValue
*/
ItemModel.prototype.setValue = function ( newValue ) {
newValue = this.coerceValue( newValue );
diff --git a/resources/src/mediawiki.rcfilters/dm/SavedQueriesModel.js b/resources/src/mediawiki.rcfilters/dm/SavedQueriesModel.js
index 0a38d690bd58..8ed737d9499e 100644
--- a/resources/src/mediawiki.rcfilters/dm/SavedQueriesModel.js
+++ b/resources/src/mediawiki.rcfilters/dm/SavedQueriesModel.js
@@ -2,16 +2,16 @@ var SavedQueryItemModel = require( './SavedQueryItemModel.js' ),
SavedQueriesModel;
/**
- * View model for saved queries
+ * View model for saved queries.
*
* @class mw.rcfilters.dm.SavedQueriesModel
+ * @ignore
* @mixins OO.EventEmitter
* @mixins OO.EmitterList
*
- * @constructor
* @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Filters model
* @param {Object} [config] Configuration options
- * @cfg {string} [default] Default query ID
+ * @param {string} [config.default] Default query ID
*/
SavedQueriesModel = function MwRcfiltersDmSavedQueriesModel( filtersModel, config ) {
config = config || {};
@@ -37,23 +37,26 @@ OO.mixinClass( SavedQueriesModel, OO.EmitterList );
/* Events */
/**
- * @event initialize
+ * Model is initialized.
*
- * Model is initialized
+ * @event initialize
+ * @ignore
*/
/**
+ * An item has changed.
+ *
* @event itemUpdate
* @param {mw.rcfilters.dm.SavedQueryItemModel} Changed item
- *
- * An item has changed
+ * @ignore
*/
/**
+ * The default has changed.
+ *
* @event default
* @param {string} New default ID
- *
- * The default has changed
+ * @ignore
*/
/* Methods */
@@ -287,6 +290,7 @@ SavedQueriesModel.prototype.removeQuery = function ( queryID ) {
*
* @param {Object} fullQueryComparison Object representing all filters and highlights to compare
* @return {mw.rcfilters.dm.SavedQueryItemModel} Matching item model
+ * @ignore
*/
SavedQueriesModel.prototype.findMatchingQuery = function ( fullQueryComparison ) {
// Minimize before comparison
@@ -308,6 +312,7 @@ SavedQueriesModel.prototype.findMatchingQuery = function ( fullQueryComparison )
/**
* Get query by its identifier
*
+ * @ignore
* @param {string} queryID Query identifier
* @return {mw.rcfilters.dm.SavedQueryItemModel|undefined} Item matching
* the search. Undefined if not found.
diff --git a/resources/src/mediawiki.rcfilters/dm/SavedQueryItemModel.js b/resources/src/mediawiki.rcfilters/dm/SavedQueryItemModel.js
index 27e93e3071b2..b07cf76c4b2d 100644
--- a/resources/src/mediawiki.rcfilters/dm/SavedQueryItemModel.js
+++ b/resources/src/mediawiki.rcfilters/dm/SavedQueryItemModel.js
@@ -1,15 +1,15 @@
/**
- * View model for a single saved query
+ * View model for a single saved query.
*
* @class mw.rcfilters.dm.SavedQueryItemModel
+ * @ignore
* @mixins OO.EventEmitter
*
- * @constructor
* @param {string} id Unique identifier
* @param {string} label Saved query label
* @param {Object} data Saved query data
* @param {Object} [config] Configuration options
- * @cfg {boolean} [default] This item is the default
+ * @param {boolean} [config.default] This item is the default
*/
var SavedQueryItemModel = function MwRcfiltersDmSavedQueriesModel( id, label, data, config ) {
config = config || {};
@@ -31,9 +31,10 @@ OO.mixinClass( SavedQueryItemModel, OO.EventEmitter );
/* Events */
/**
- * @event update
+ * Model has been updated.
*
- * Model has been updated
+ * @event update
+ * @ignore
*/
/* Methods */
diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.js b/resources/src/mediawiki.rcfilters/mw.rcfilters.js
index 88590925b52b..56caf3767c89 100644
--- a/resources/src/mediawiki.rcfilters/mw.rcfilters.js
+++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.js
@@ -33,6 +33,12 @@ const rcfilters = {
ui: {
MainWrapperWidget: require( './ui/MainWrapperWidget.js' )
},
+ /**
+ * Utils used by RecentChanges Filters.
+ *
+ * @namespace rcfilters.ui
+ * @private
+ */
utils: require( './utils.js' )
};
@@ -169,8 +175,8 @@ function init() {
/**
* Fired when initialization of the filtering interface for changes list is complete.
*
- * @event structuredChangeFilters_ui_initialized
- * @member mw.hook
+ * @event ~'structuredChangeFilters.ui.initialized'
+ * @memberof Hooks
*/
mw.hook( 'structuredChangeFilters.ui.initialized' ).fire();
}
diff --git a/resources/src/mediawiki.rcfilters/ui/ChangesLimitAndDateButtonWidget.js b/resources/src/mediawiki.rcfilters/ui/ChangesLimitAndDateButtonWidget.js
index 5e302b5508b5..323f8833a0f8 100644
--- a/resources/src/mediawiki.rcfilters/ui/ChangesLimitAndDateButtonWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/ChangesLimitAndDateButtonWidget.js
@@ -3,16 +3,16 @@ var ChangesLimitPopupWidget = require( './ChangesLimitPopupWidget.js' ),
ChangesLimitAndDateButtonWidget;
/**
- * Widget defining the button controlling the popup for the number of results
+ * Widget defining the button controlling the popup for the number of results.
*
* @class mw.rcfilters.ui.ChangesLimitAndDateButtonWidget
+ * @ignore
* @extends OO.ui.Widget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller Controller
* @param {mw.rcfilters.dm.FiltersViewModel} model View model
* @param {Object} [config] Configuration object
- * @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
+ * @param {jQuery} [config.$overlay] A jQuery object serving as overlay for popups
*/
ChangesLimitAndDateButtonWidget = function MwRcfiltersUiChangesLimitWidget( controller, model, config ) {
config = config || {};
diff --git a/resources/src/mediawiki.rcfilters/ui/ChangesLimitPopupWidget.js b/resources/src/mediawiki.rcfilters/ui/ChangesLimitPopupWidget.js
index 9c293d7f05cb..0e00977909e6 100644
--- a/resources/src/mediawiki.rcfilters/ui/ChangesLimitPopupWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/ChangesLimitPopupWidget.js
@@ -2,12 +2,12 @@ var ValuePickerWidget = require( './ValuePickerWidget.js' ),
ChangesLimitPopupWidget;
/**
- * Widget defining the popup to choose number of results
+ * Widget defining the popup to choose number of results.
*
* @class mw.rcfilters.ui.ChangesLimitPopupWidget
+ * @ignore
* @extends OO.ui.Widget
*
- * @constructor
* @param {mw.rcfilters.dm.FilterGroup} limitModel Group model for 'limit'
* @param {mw.rcfilters.dm.FilterItem} groupByPageItemModel Group model for 'limit'
* @param {Object} [config] Configuration object
@@ -67,17 +67,19 @@ OO.inheritClass( ChangesLimitPopupWidget, OO.ui.Widget );
/* Events */
/**
+ * A limit item was chosen.
+ *
* @event limit
* @param {string} name Item name
- *
- * A limit item was chosen
+ * @ignore
*/
/**
+ * Results are grouped by page
+ *
* @event groupByPage
* @param {boolean} isGrouped The results are grouped by page
- *
- * Results are grouped by page
+ * @ignore
*/
/**
diff --git a/resources/src/mediawiki.rcfilters/ui/ChangesListWrapperWidget.js b/resources/src/mediawiki.rcfilters/ui/ChangesListWrapperWidget.js
index 54069d6d302b..dcadf3d84a03 100644
--- a/resources/src/mediawiki.rcfilters/ui/ChangesListWrapperWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/ChangesListWrapperWidget.js
@@ -1,12 +1,12 @@
var HighlightColors = require( '../HighlightColors.js' );
/**
- * List of changes
+ * List of changes.
*
* @class mw.rcfilters.ui.ChangesListWrapperWidget
+ * @ignore
* @extends OO.ui.Widget
*
- * @constructor
* @param {mw.rcfilters.dm.FiltersViewModel} filtersViewModel View model
* @param {mw.rcfilters.dm.ChangesListViewModel} changesListViewModel View model
* @param {mw.rcfilters.Controller} controller
diff --git a/resources/src/mediawiki.rcfilters/ui/CheckboxInputWidget.js b/resources/src/mediawiki.rcfilters/ui/CheckboxInputWidget.js
index 4ef662517207..d497739dbca9 100644
--- a/resources/src/mediawiki.rcfilters/ui/CheckboxInputWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/CheckboxInputWidget.js
@@ -1,10 +1,10 @@
/**
- * A widget representing a single toggle filter
+ * A widget representing a single toggle filter.
*
* @class mw.rcfilters.ui.CheckboxInputWidget
+ * @ignore
* @extends OO.ui.CheckboxInputWidget
*
- * @constructor
* @param {Object} config Configuration object
*/
var CheckboxInputWidget = function MwRcfiltersUiCheckboxInputWidget( config ) {
@@ -33,10 +33,11 @@ OO.inheritClass( CheckboxInputWidget, OO.ui.CheckboxInputWidget );
/* Events */
/**
+ * The user has checked or unchecked this checkbox.
+ *
* @event userChange
* @param {boolean} Current state of the checkbox
- *
- * The user has checked or unchecked this checkbox
+ * @ignore
*/
/* Methods */
diff --git a/resources/src/mediawiki.rcfilters/ui/DatePopupWidget.js b/resources/src/mediawiki.rcfilters/ui/DatePopupWidget.js
index bae2e3c4ac97..761984b33242 100644
--- a/resources/src/mediawiki.rcfilters/ui/DatePopupWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/DatePopupWidget.js
@@ -2,12 +2,12 @@ var ValuePickerWidget = require( './ValuePickerWidget.js' ),
DatePopupWidget;
/**
- * Widget defining the popup to choose date for the results
+ * Widget defining the popup to choose date for the results.
*
* @class mw.rcfilters.ui.DatePopupWidget
+ * @ignore
* @extends OO.ui.Widget
*
- * @constructor
* @param {mw.rcfilters.dm.FilterGroup} model Group model for 'days'
* @param {Object} [config] Configuration object
*/
@@ -68,10 +68,11 @@ OO.mixinClass( DatePopupWidget, OO.ui.mixin.LabelElement );
/* Events */
/**
+ * A days item was chosen
+ *
* @event days
* @param {string} name Item name
- *
- * A days item was chosen
+ * @ignore
*/
module.exports = DatePopupWidget;
diff --git a/resources/src/mediawiki.rcfilters/ui/FilterItemHighlightButton.js b/resources/src/mediawiki.rcfilters/ui/FilterItemHighlightButton.js
index 1f35bfd3a817..bafba3ebf5c9 100644
--- a/resources/src/mediawiki.rcfilters/ui/FilterItemHighlightButton.js
+++ b/resources/src/mediawiki.rcfilters/ui/FilterItemHighlightButton.js
@@ -1,12 +1,12 @@
var HighlightColors = require( '../HighlightColors.js' );
/**
- * A button to configure highlight for a filter item
+ * A button to configure highlight for a filter item.
*
* @class mw.rcfilters.ui.FilterItemHighlightButton
+ * @ignore
* @extends OO.ui.PopupButtonWidget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller RCFilters controller
* @param {mw.rcfilters.dm.FilterItem} model Filter item model
* @param {mw.rcfilters.ui.HighlightPopupWidget} highlightPopup Shared highlight color picker
diff --git a/resources/src/mediawiki.rcfilters/ui/FilterMenuHeaderWidget.js b/resources/src/mediawiki.rcfilters/ui/FilterMenuHeaderWidget.js
index 00ff6b5bb8a0..5cfe8f2dc40e 100644
--- a/resources/src/mediawiki.rcfilters/ui/FilterMenuHeaderWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/FilterMenuHeaderWidget.js
@@ -1,14 +1,14 @@
/**
- * Menu header for the RCFilters filters menu
+ * Menu header for the RCFilters filters menu.
*
* @class mw.rcfilters.ui.FilterMenuHeaderWidget
+ * @ignore
* @extends OO.ui.Widget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller Controller
* @param {mw.rcfilters.dm.FiltersViewModel} model View model
* @param {Object} config Configuration object
- * @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
+ * @param {jQuery} [config.$overlay] A jQuery object serving as overlay for popups
*/
var FilterMenuHeaderWidget = function MwRcfiltersUiFilterMenuHeaderWidget( controller, model, config ) {
config = config || {};
diff --git a/resources/src/mediawiki.rcfilters/ui/FilterMenuOptionWidget.js b/resources/src/mediawiki.rcfilters/ui/FilterMenuOptionWidget.js
index a74fa2e71271..97539429f7af 100644
--- a/resources/src/mediawiki.rcfilters/ui/FilterMenuOptionWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/FilterMenuOptionWidget.js
@@ -2,12 +2,12 @@ var ItemMenuOptionWidget = require( './ItemMenuOptionWidget.js' ),
FilterMenuOptionWidget;
/**
- * A widget representing a single toggle filter
+ * A widget representing a single toggle filter.
*
* @class mw.rcfilters.ui.FilterMenuOptionWidget
+ * @ignore
* @extends mw.rcfilters.ui.ItemMenuOptionWidget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller RCFilters controller
* @param {mw.rcfilters.dm.FiltersViewModel} filtersViewModel
* @param {mw.rcfilters.dm.FilterItem|null} invertModel
diff --git a/resources/src/mediawiki.rcfilters/ui/FilterMenuSectionOptionWidget.js b/resources/src/mediawiki.rcfilters/ui/FilterMenuSectionOptionWidget.js
index a4172ae01d9d..baf6166a47d7 100644
--- a/resources/src/mediawiki.rcfilters/ui/FilterMenuSectionOptionWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/FilterMenuSectionOptionWidget.js
@@ -1,14 +1,14 @@
/**
- * A widget representing a menu section for filter groups
+ * A widget representing a menu section for filter groups.
*
* @class mw.rcfilters.ui.FilterMenuSectionOptionWidget
+ * @ignore
* @extends OO.ui.MenuSectionOptionWidget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller RCFilters controller
* @param {mw.rcfilters.dm.FilterGroup} model Filter group model
* @param {Object} config Configuration object
- * @cfg {jQuery} [$overlay] Overlay
+ * @param {jQuery} [config.$overlay] Overlay
*/
var FilterMenuSectionOptionWidget = function MwRcfiltersUiFilterMenuSectionOptionWidget( controller, model, config ) {
var whatsThisMessages,
diff --git a/resources/src/mediawiki.rcfilters/ui/FilterTagItemWidget.js b/resources/src/mediawiki.rcfilters/ui/FilterTagItemWidget.js
index 15efe81c53a2..18bf649cf595 100644
--- a/resources/src/mediawiki.rcfilters/ui/FilterTagItemWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/FilterTagItemWidget.js
@@ -5,9 +5,9 @@ var TagItemWidget = require( './TagItemWidget.js' ),
* Extend OOUI's FilterTagItemWidget to also display a popup on hover.
*
* @class mw.rcfilters.ui.FilterTagItemWidget
+ * @ignore
* @extends mw.rcfilters.ui.TagItemWidget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller
* @param {mw.rcfilters.dm.FiltersViewModel} filtersViewModel
* @param {mw.rcfilters.dm.FilterItem} invertModel
diff --git a/resources/src/mediawiki.rcfilters/ui/FilterTagMultiselectWidget.js b/resources/src/mediawiki.rcfilters/ui/FilterTagMultiselectWidget.js
index 2828f594a2fb..b4f632d5d6c4 100644
--- a/resources/src/mediawiki.rcfilters/ui/FilterTagMultiselectWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/FilterTagMultiselectWidget.js
@@ -5,21 +5,21 @@ var ViewSwitchWidget = require( './ViewSwitchWidget.js' ),
FilterTagMultiselectWidget;
/**
- * List displaying all filter groups
+ * List displaying all filter groups.
*
* @class mw.rcfilters.ui.FilterTagMultiselectWidget
+ * @ignore
* @extends OO.ui.MenuTagMultiselectWidget
* @mixins OO.ui.mixin.PendingElement
*
- * @constructor
* @param {mw.rcfilters.Controller} controller Controller
* @param {mw.rcfilters.dm.FiltersViewModel} model View model
* @param {mw.rcfilters.dm.SavedQueriesModel} savedQueriesModel Saved queries model
* @param {Object} config Configuration object
- * @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
- * @cfg {jQuery} [$wrapper] A jQuery object for the wrapper of the general
+ * @param {jQuery} [config.$overlay] A jQuery object serving as overlay for popups
+ * @param {jQuery} [config.$wrapper] A jQuery object for the wrapper of the general
* system. If not given, falls back to this widget's $element
- * @cfg {boolean} [collapsed] Filter area is collapsed
+ * @param {boolean} [config.collapsed] Filter area is collapsed
*/
FilterTagMultiselectWidget = function MwRcfiltersUiFilterTagMultiselectWidget( controller, model, savedQueriesModel, config ) {
var $rcFiltersRow,
@@ -82,6 +82,7 @@ FilterTagMultiselectWidget = function MwRcfiltersUiFilterTagMultiselectWidget( c
* rather than a text input. Mobile screens are too small to accommodate both an
* onscreen keyboard and a popup-menu, so readyOnly is set to disable the keyboard.
* A different icon and shorter message is used for mobile as well. (See T224655 for details).
+ * @ignore
*/
input: {
icon: this.isMobile ? 'funnel' : 'menu',
@@ -394,6 +395,12 @@ FilterTagMultiselectWidget.prototype.onMenuToggle = function ( isVisible ) {
this.focus();
}
+ /**
+ * Fires when the RCFilters tag multi selector menu is toggled.
+ *
+ * @event ~'RcFilters.popup.open'
+ * @memberof Hooks
+ */
mw.hook( 'RcFilters.popup.open' ).fire();
if ( !this.getMenu().findSelectedItem() ) {
diff --git a/resources/src/mediawiki.rcfilters/ui/FilterWrapperWidget.js b/resources/src/mediawiki.rcfilters/ui/FilterWrapperWidget.js
index 8bbc47c2e779..dc887faaab00 100644
--- a/resources/src/mediawiki.rcfilters/ui/FilterWrapperWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/FilterWrapperWidget.js
@@ -4,23 +4,23 @@ var FilterTagMultiselectWidget = require( './FilterTagMultiselectWidget.js' ),
FilterWrapperWidget;
/**
- * List displaying all filter groups
+ * List displaying all filter groups.
*
* @class mw.rcfilters.ui.FilterWrapperWidget
+ * @ignore
* @extends OO.ui.Widget
* @mixins OO.ui.mixin.PendingElement
*
- * @constructor
* @param {mw.rcfilters.Controller} controller Controller
* @param {mw.rcfilters.dm.FiltersViewModel} model View model
* @param {mw.rcfilters.dm.SavedQueriesModel} savedQueriesModel Saved queries model
* @param {mw.rcfilters.dm.ChangesListViewModel} changesListModel
* @param {Object} [config] Configuration object
- * @cfg {Object} [filters] A definition of the filter groups in this list
- * @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
- * @cfg {jQuery} [$wrapper] A jQuery object for the wrapper of the general
+ * @param {Object} [config.filters] A definition of the filter groups in this list
+ * @param {jQuery} [config.$overlay] A jQuery object serving as overlay for popups
+ * @param {jQuery} [config.$wrapper] A jQuery object for the wrapper of the general
* system. If not given, falls back to this widget's $element
- * @cfg {boolean} [collapsed] Filter area is collapsed
+ * @param {boolean} [config.collapsed] Filter area is collapsed
*/
FilterWrapperWidget = function MwRcfiltersUiFilterWrapperWidget(
controller, model, savedQueriesModel, changesListModel, config
diff --git a/resources/src/mediawiki.rcfilters/ui/FormWrapperWidget.js b/resources/src/mediawiki.rcfilters/ui/FormWrapperWidget.js
index 3318f40050ff..0f5a46e1d2f4 100644
--- a/resources/src/mediawiki.rcfilters/ui/FormWrapperWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/FormWrapperWidget.js
@@ -3,9 +3,9 @@
* Must be constructed after the model is initialized.
*
* @class mw.rcfilters.ui.FormWrapperWidget
+ * @ignore
* @extends OO.ui.Widget
*
- * @constructor
* @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Changes list view model
* @param {mw.rcfilters.dm.ChangesListViewModel} changeListModel Changes list view model
* @param {mw.rcfilters.Controller} controller RCfilters controller
diff --git a/resources/src/mediawiki.rcfilters/ui/HighlightColorPickerWidget.js b/resources/src/mediawiki.rcfilters/ui/HighlightColorPickerWidget.js
index 6d2c57a362dc..e7299cbf57b1 100644
--- a/resources/src/mediawiki.rcfilters/ui/HighlightColorPickerWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/HighlightColorPickerWidget.js
@@ -1,13 +1,13 @@
var HighlightColors = require( '../HighlightColors.js' );
/**
- * A widget representing a filter item highlight color picker
+ * A widget representing a filter item highlight color picker.
*
* @class mw.rcfilters.ui.HighlightColorPickerWidget
+ * @ignore
* @extends OO.ui.Widget
* @mixins OO.ui.mixin.LabelElement
*
- * @constructor
* @param {mw.rcfilters.Controller} controller RCFilters controller
* @param {Object} [config] Configuration object
*/
@@ -66,10 +66,11 @@ OO.mixinClass( HighlightColorPickerWidget, OO.ui.mixin.LabelElement );
/* Events */
/**
+ * A color has been chosen
+ *
* @event chooseColor
* @param {string} The chosen color
- *
- * A color has been chosen
+ * @ignore
*/
/* Methods */
diff --git a/resources/src/mediawiki.rcfilters/ui/HighlightPopupWidget.js b/resources/src/mediawiki.rcfilters/ui/HighlightPopupWidget.js
index 773bacbf6e26..feed81634b81 100644
--- a/resources/src/mediawiki.rcfilters/ui/HighlightPopupWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/HighlightPopupWidget.js
@@ -4,9 +4,9 @@ var HighlightColorPickerWidget = require( './HighlightColorPickerWidget.js' ),
* A popup containing a color picker, for setting highlight colors.
*
* @class mw.rcfilters.ui.HighlightPopupWidget
+ * @ignore
* @extends OO.ui.PopupWidget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller RCFilters controller
* @param {Object} [config] Configuration object
*/
diff --git a/resources/src/mediawiki.rcfilters/ui/ItemMenuOptionWidget.js b/resources/src/mediawiki.rcfilters/ui/ItemMenuOptionWidget.js
index 5e91c1d2dd02..992d4b968111 100644
--- a/resources/src/mediawiki.rcfilters/ui/ItemMenuOptionWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/ItemMenuOptionWidget.js
@@ -3,12 +3,12 @@ var FilterItemHighlightButton = require( './FilterItemHighlightButton.js' ),
ItemMenuOptionWidget;
/**
- * A widget representing a base toggle item
+ * A widget representing a base toggle item.
*
* @class mw.rcfilters.ui.ItemMenuOptionWidget
+ * @ignore
* @extends OO.ui.MenuOptionWidget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller RCFilters controller
* @param {mw.rcfilters.dm.FiltersViewModel} filtersViewModel
* @param {mw.rcfilters.dm.ItemModel|null} invertModel
diff --git a/resources/src/mediawiki.rcfilters/ui/LiveUpdateButtonWidget.js b/resources/src/mediawiki.rcfilters/ui/LiveUpdateButtonWidget.js
index 2c1b3356df99..d2ee6eb3902f 100644
--- a/resources/src/mediawiki.rcfilters/ui/LiveUpdateButtonWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/LiveUpdateButtonWidget.js
@@ -1,10 +1,10 @@
/**
- * Widget for toggling live updates
+ * Widget for toggling live updates.
*
* @class mw.rcfilters.ui.LiveUpdateButtonWidget
+ * @ignore
* @extends OO.ui.ToggleButtonWidget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller
* @param {mw.rcfilters.dm.ChangesListViewModel} changesListModel
* @param {Object} [config] Configuration object
diff --git a/resources/src/mediawiki.rcfilters/ui/MainWrapperWidget.js b/resources/src/mediawiki.rcfilters/ui/MainWrapperWidget.js
index 8eb5ed549477..e3af3df6e063 100644
--- a/resources/src/mediawiki.rcfilters/ui/MainWrapperWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/MainWrapperWidget.js
@@ -8,23 +8,23 @@ var SavedLinksListWidget = require( './SavedLinksListWidget.js' ),
MainWrapperWidget;
/**
- * Wrapper for changes list content
+ * Wrapper for changes list content.
*
* @class mw.rcfilters.ui.MainWrapperWidget
+ * @ignore
* @extends OO.ui.Widget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller Controller
* @param {mw.rcfilters.dm.FiltersViewModel} model View model
* @param {mw.rcfilters.dm.SavedQueriesModel} savedQueriesModel Saved queries model
* @param {mw.rcfilters.dm.ChangesListViewModel} changesListModel
* @param {Object} config Configuration object
- * @cfg {jQuery} $topSection Top section container
- * @cfg {jQuery} $filtersContainer
- * @cfg {jQuery} $changesListContainer
- * @cfg {jQuery} $formContainer
- * @cfg {boolean} [collapsed] Filter area is collapsed
- * @cfg {jQuery} [$wrapper] A jQuery object for the wrapper of the general
+ * @param {jQuery} config.$topSection Top section container
+ * @param {jQuery} config.$filtersContainer
+ * @param {jQuery} config.$changesListContainer
+ * @param {jQuery} config.$formContainer
+ * @param {boolean} [config.collapsed] Filter area is collapsed
+ * @param {jQuery} [config.$wrapper] A jQuery object for the wrapper of the general
* system. If not given, falls back to this widget's $element
*/
MainWrapperWidget = function MwRcfiltersUiMainWrapperWidget(
@@ -129,6 +129,7 @@ MainWrapperWidget.prototype.onFilterMenuToggle = function ( isVisible ) {
/**
* Initialize FormWrapperWidget
*
+ * @ignore
* @return {mw.rcfilters.ui.FormWrapperWidget} Form wrapper widget
*/
MainWrapperWidget.prototype.initFormWidget = function () {
diff --git a/resources/src/mediawiki.rcfilters/ui/MarkSeenButtonWidget.js b/resources/src/mediawiki.rcfilters/ui/MarkSeenButtonWidget.js
index dab97800984c..5d2eba135844 100644
--- a/resources/src/mediawiki.rcfilters/ui/MarkSeenButtonWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/MarkSeenButtonWidget.js
@@ -1,10 +1,10 @@
/**
- * Button for marking all changes as seen on the Watchlist
+ * Button for marking all changes as seen on the Watchlist.
*
* @class mw.rcfilters.ui.MarkSeenButtonWidget
+ * @ignore
* @extends OO.ui.ButtonWidget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller
* @param {mw.rcfilters.dm.ChangesListViewModel} model Changes list view model
* @param {Object} [config] Configuration object
diff --git a/resources/src/mediawiki.rcfilters/ui/MenuSelectWidget.js b/resources/src/mediawiki.rcfilters/ui/MenuSelectWidget.js
index ac013f3705a9..97a553277140 100644
--- a/resources/src/mediawiki.rcfilters/ui/MenuSelectWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/MenuSelectWidget.js
@@ -5,19 +5,19 @@ var FilterMenuHeaderWidget = require( './FilterMenuHeaderWidget.js' ),
MenuSelectWidget;
/**
- * A floating menu widget for the filter list
+ * A floating menu widget for the filter list.
*
* @class mw.rcfilters.ui.MenuSelectWidget
+ * @ignore
* @extends OO.ui.MenuSelectWidget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller Controller
* @param {mw.rcfilters.dm.FiltersViewModel} model View model
* @param {Object} [config] Configuration object
- * @cfg {boolean} [isMobile] a boolean flag determining whether the menu
+ * @param {boolean} [config.isMobile] a boolean flag determining whether the menu
* should display a header or not (the header is omitted on mobile).
- * @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
- * @cfg {Object[]} [footers] An array of objects defining the footers for
+ * @param {jQuery} [config.$overlay] A jQuery object serving as overlay for popups
+ * @param {Object[]} [config.footers] An array of objects defining the footers for
* this menu, with a definition whether they appear per specific views.
* The expected structure is:
* [
@@ -314,6 +314,7 @@ MenuSelectWidget.prototype.postProcessItems = function () {
/**
* Get the option widget that matches the model given
*
+ * @ignore
* @param {mw.rcfilters.dm.ItemModel} model Item model
* @return {mw.rcfilters.ui.ItemMenuOptionWidget} Option widget
*/
diff --git a/resources/src/mediawiki.rcfilters/ui/RcTopSectionWidget.js b/resources/src/mediawiki.rcfilters/ui/RcTopSectionWidget.js
index e2200ec7bd18..588d9f01a957 100644
--- a/resources/src/mediawiki.rcfilters/ui/RcTopSectionWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/RcTopSectionWidget.js
@@ -1,10 +1,10 @@
/**
- * Top section (between page title and filters) on Special:Recentchanges
+ * Top section (between page title and filters) on Special:Recentchanges.
*
* @class mw.rcfilters.ui.RcTopSectionWidget
+ * @ignore
* @extends OO.ui.Widget
*
- * @constructor
* @param {mw.rcfilters.ui.SavedLinksListWidget} savedLinksListWidget
* @param {jQuery} $topLinks Content of the community-defined links
* @param {Object} [config] Configuration object
diff --git a/resources/src/mediawiki.rcfilters/ui/RclTargetPageWidget.js b/resources/src/mediawiki.rcfilters/ui/RclTargetPageWidget.js
index e47d7890ab17..11620bdc4724 100644
--- a/resources/src/mediawiki.rcfilters/ui/RclTargetPageWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/RclTargetPageWidget.js
@@ -1,10 +1,10 @@
/**
- * Widget to select and display target page on Special:RecentChangesLinked (AKA Related Changes)
+ * Widget to select and display target page on Special:RecentChangesLinked (AKA Related Changes).
*
* @class mw.rcfilters.ui.RclTargetPageWidget
+ * @ignore
* @extends OO.ui.Widget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller
* @param {mw.rcfilters.dm.FilterItem} targetPageModel
* @param {Object} [config] Configuration object
diff --git a/resources/src/mediawiki.rcfilters/ui/RclToOrFromWidget.js b/resources/src/mediawiki.rcfilters/ui/RclToOrFromWidget.js
index 1b59a9309e9a..9c2262e57667 100644
--- a/resources/src/mediawiki.rcfilters/ui/RclToOrFromWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/RclToOrFromWidget.js
@@ -1,11 +1,11 @@
/**
* Widget to select to view changes that link TO or FROM the target page
- * on Special:RecentChangesLinked (AKA Related Changes)
+ * on Special:RecentChangesLinked (AKA Related Changes).
*
* @class mw.rcfilters.ui.RclToOrFromWidget
+ * @ignore
* @extends OO.ui.DropdownWidget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller
* @param {mw.rcfilters.dm.FilterItem} showLinkedToModel model this widget is bound to
* @param {Object} [config] Configuration object
diff --git a/resources/src/mediawiki.rcfilters/ui/RclTopSectionWidget.js b/resources/src/mediawiki.rcfilters/ui/RclTopSectionWidget.js
index 78211e6265cb..edc7d3366374 100644
--- a/resources/src/mediawiki.rcfilters/ui/RclTopSectionWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/RclTopSectionWidget.js
@@ -3,12 +3,12 @@ var RclToOrFromWidget = require( './RclToOrFromWidget.js' ),
RclTopSectionWidget;
/**
- * Top section (between page title and filters) on Special:RecentChangesLinked (AKA RelatedChanges)
+ * Top section (between page title and filters) on Special:RecentChangesLinked (AKA RelatedChanges).
*
* @class mw.rcfilters.ui.RclTopSectionWidget
+ * @ignore
* @extends OO.ui.Widget
*
- * @constructor
* @param {mw.rcfilters.ui.SavedLinksListWidget} savedLinksListWidget
* @param {mw.rcfilters.Controller} controller
* @param {mw.rcfilters.dm.FilterItem} showLinkedToModel Model for 'showlinkedto' parameter
diff --git a/resources/src/mediawiki.rcfilters/ui/SaveFiltersPopupButtonWidget.js b/resources/src/mediawiki.rcfilters/ui/SaveFiltersPopupButtonWidget.js
index d51257297c42..e6ce452892c1 100644
--- a/resources/src/mediawiki.rcfilters/ui/SaveFiltersPopupButtonWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/SaveFiltersPopupButtonWidget.js
@@ -5,9 +5,9 @@
* default.
*
* @class mw.rcfilters.ui.SaveFiltersPopupButtonWidget
+ * @ignore
* @extends OO.ui.PopupButtonWidget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller Controller
* @param {mw.rcfilters.dm.SavedQueriesModel} model View model
* @param {Object} [config] Configuration object
diff --git a/resources/src/mediawiki.rcfilters/ui/SavedLinksListItemWidget.js b/resources/src/mediawiki.rcfilters/ui/SavedLinksListItemWidget.js
index f5e021f0b716..3ed42e15c89b 100644
--- a/resources/src/mediawiki.rcfilters/ui/SavedLinksListItemWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/SavedLinksListItemWidget.js
@@ -1,13 +1,13 @@
/**
- * Quick links menu option widget
+ * Quick links menu option widget.
*
* @class mw.rcfilters.ui.SavedLinksListItemWidget
+ * @ignore
* @extends OO.ui.Widget
*
- * @constructor
* @param {mw.rcfilters.dm.SavedQueryItemModel} model View model
* @param {Object} [config] Configuration object
- * @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
+ * @param {jQuery} [config.$overlay] A jQuery object serving as overlay for popups
*/
var SavedLinksListItemWidget = function MwRcfiltersUiSavedLinksListWidget( model, config ) {
config = config || {};
@@ -123,23 +123,26 @@ OO.inheritClass( SavedLinksListItemWidget, OO.ui.MenuOptionWidget );
/* Events */
/**
- * @event delete
+ * The delete option was selected for this item.
*
- * The delete option was selected for this item
+ * @event delete
+ * @ignore
*/
/**
+ * The 'make default' option was selected for this item.
+ *
* @event default
* @param {boolean} default Item is default
- *
- * The 'make default' option was selected for this item
+ * @ignore
*/
/**
+ * The label has been edited.
+ *
* @event edit
* @param {string} newLabel New label for the query
- *
- * The label has been edited
+ * @ignore
*/
/* Methods */
diff --git a/resources/src/mediawiki.rcfilters/ui/SavedLinksListWidget.js b/resources/src/mediawiki.rcfilters/ui/SavedLinksListWidget.js
index c0c236c60164..4b9f0e66e4ec 100644
--- a/resources/src/mediawiki.rcfilters/ui/SavedLinksListWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/SavedLinksListWidget.js
@@ -2,16 +2,15 @@ var SavedLinksListItemWidget = require( './SavedLinksListItemWidget.js' ),
SavedLinksListWidget;
/**
- * Quick links widget
+ * Quick links widget.
*
* @class mw.rcfilters.ui.SavedLinksListWidget
+ * @ignore
* @extends OO.ui.ButtonMenuSelectWidget
- *
- * @constructor
* @param {mw.rcfilters.Controller} controller Controller
* @param {mw.rcfilters.dm.SavedQueriesModel} model View model
* @param {Object} [config] Configuration object
- * @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
+ * @param {jQuery} [config.$overlay] A jQuery object serving as overlay for popups
*/
SavedLinksListWidget = function MwRcfiltersUiSavedLinksListWidget( controller, model, config ) {
var $labelNoEntries = $( '<div>' )
diff --git a/resources/src/mediawiki.rcfilters/ui/TagItemWidget.js b/resources/src/mediawiki.rcfilters/ui/TagItemWidget.js
index 4566195e10c0..fb5e98457e24 100644
--- a/resources/src/mediawiki.rcfilters/ui/TagItemWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/TagItemWidget.js
@@ -2,16 +2,16 @@
* Extend OOUI's TagItemWidget to also display a popup on hover.
*
* @class mw.rcfilters.ui.TagItemWidget
+ * @ignore
* @extends OO.ui.TagItemWidget
* @mixins OO.ui.mixin.PopupElement
*
- * @constructor
* @param {mw.rcfilters.Controller} controller
* @param {mw.rcfilters.dm.FiltersViewModel} filtersViewModel
* @param {mw.rcfilters.dm.FilterItem|null} invertModel
* @param {mw.rcfilters.dm.FilterItem} itemModel Item model
* @param {Object} config Configuration object
- * @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
+ * @param {jQuery} [config.$overlay] A jQuery object serving as overlay for popups
*/
var TagItemWidget = function MwRcfiltersUiTagItemWidget(
controller, filtersViewModel, invertModel, itemModel, config
diff --git a/resources/src/mediawiki.rcfilters/ui/ValuePickerWidget.js b/resources/src/mediawiki.rcfilters/ui/ValuePickerWidget.js
index 5c03785fe56f..66761990d63a 100644
--- a/resources/src/mediawiki.rcfilters/ui/ValuePickerWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/ValuePickerWidget.js
@@ -1,15 +1,15 @@
/**
* Widget defining the behavior used to choose from a set of values
- * in a single_value group
+ * in a single_value group.
*
* @class mw.rcfilters.ui.ValuePickerWidget
+ * @ignore
* @extends OO.ui.Widget
* @mixins OO.ui.mixin.LabelElement
*
- * @constructor
* @param {mw.rcfilters.dm.FilterGroup} model Group model
* @param {Object} [config] Configuration object
- * @cfg {Function} [itemFilter] A filter function for the items from the
+ * @param {Function} [config.itemFilter] A filter function for the items from the
* model. If not given, all items will be included. The function must
* handle item models and return a boolean whether the item is included
* or not. Example: function ( itemModel ) { return itemModel.isSelected(); }
@@ -53,10 +53,11 @@ OO.mixinClass( ValuePickerWidget, OO.ui.mixin.LabelElement );
/* Events */
/**
+ * An item has been chosen.
+ *
* @event choose
* @param {string} name Item name
- *
- * An item has been chosen
+ * @ignore
*/
/* Methods */
diff --git a/resources/src/mediawiki.rcfilters/ui/ViewSwitchWidget.js b/resources/src/mediawiki.rcfilters/ui/ViewSwitchWidget.js
index af2d6c1dcbea..e9849c2ac0b2 100644
--- a/resources/src/mediawiki.rcfilters/ui/ViewSwitchWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/ViewSwitchWidget.js
@@ -1,12 +1,12 @@
var ViewSwitchWidget;
/**
- * A widget for the footer for the default view, allowing to switch views
+ * A widget for the footer for the default view, allowing to switch views.
*
* @class mw.rcfilters.ui.ViewSwitchWidget
+ * @ignore
* @extends OO.ui.Widget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller Controller
* @param {mw.rcfilters.dm.FiltersViewModel} model View model
* @param {Object} [config] Configuration object
diff --git a/resources/src/mediawiki.rcfilters/ui/WatchlistTopSectionWidget.js b/resources/src/mediawiki.rcfilters/ui/WatchlistTopSectionWidget.js
index 734d2d11cfa9..2e260a3e47c3 100644
--- a/resources/src/mediawiki.rcfilters/ui/WatchlistTopSectionWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/WatchlistTopSectionWidget.js
@@ -1,12 +1,12 @@
var MarkSeenButtonWidget = require( './MarkSeenButtonWidget.js' ),
WatchlistTopSectionWidget;
/**
- * Top section (between page title and filters) on Special:Watchlist
+ * Top section (between page title and filters) on Special:Watchlist.
*
* @class mw.rcfilters.ui.WatchlistTopSectionWidget
+ * @ignore
* @extends OO.ui.Widget
*
- * @constructor
* @param {mw.rcfilters.Controller} controller
* @param {mw.rcfilters.dm.ChangesListViewModel} changesListModel
* @param {mw.rcfilters.ui.SavedLinksListWidget} savedLinksListWidget
diff --git a/resources/src/mediawiki.rcfilters/utils.js b/resources/src/mediawiki.rcfilters/utils.js
index 80ac1cc3bfcc..f571f447cf65 100644
--- a/resources/src/mediawiki.rcfilters/utils.js
+++ b/resources/src/mediawiki.rcfilters/utils.js
@@ -6,7 +6,7 @@
*/
module.exports = {
/**
- * @param {Node[]} elements
+ * @param {Node[]} arr
* @param {Node[]|Node} elements
* @return {{Node[]}
*/
diff --git a/resources/src/mediawiki.skinning/i18n-headings.less b/resources/src/mediawiki.skinning/i18n-headings.less
index 558e04dbbecd..0fa5fa8b70f9 100644
--- a/resources/src/mediawiki.skinning/i18n-headings.less
+++ b/resources/src/mediawiki.skinning/i18n-headings.less
@@ -29,10 +29,12 @@
:lang( syl ),
:lang( ta ),
:lang( te ) {
+ .mw-heading1&,
h1& {
line-height: 1.6em !important; /* stylelint-disable-line declaration-no-important */
}
+ .mw-heading&,
h2&,
h3&,
h4&,
@@ -43,6 +45,7 @@
}
// Increase for Nepalese on `h1`, See T333938.
+.mw-heading1:lang( ne ),
h1:lang( ne ) {
line-height: 1.9;
}
@@ -50,6 +53,8 @@ h1:lang( ne ) {
// Increase `line-height` for Thai top vowel + top tone combination on `h2`,
// for example on `ตั้`. See T285972.
// Increase for Nepalese on `h2`, for example on `ि`. See T333938.
+.mw-heading2:lang( th ),
+.mw-heading2:lang( ne ),
h2:lang( th ),
h2:lang( ne ) {
line-height: 1.6;
diff --git a/resources/src/mediawiki.special.preferences.ooui/nav.js b/resources/src/mediawiki.special.preferences.ooui/nav.js
index 3f73137fabea..89a71c5a6cd0 100644
--- a/resources/src/mediawiki.special.preferences.ooui/nav.js
+++ b/resources/src/mediawiki.special.preferences.ooui/nav.js
@@ -16,9 +16,10 @@
*
* @ignore
* @param {string} hintMsg the layout-specific navigation hint message
+ * @return {jQuery}
*/
insertHints: function ( hintMsg ) {
- $( '<div>' ).addClass( 'mw-navigation-hint' )
+ return $( '<div>' ).addClass( 'mw-navigation-hint' )
.text( hintMsg )
.attr( {
tabIndex: 0
diff --git a/resources/src/mediawiki.special.preferences.ooui/tabs.js b/resources/src/mediawiki.special.preferences.ooui/tabs.js
index e73c8865477c..dc95be64b948 100644
--- a/resources/src/mediawiki.special.preferences.ooui/tabs.js
+++ b/resources/src/mediawiki.special.preferences.ooui/tabs.js
@@ -4,7 +4,7 @@
( function () {
var nav = require( './nav.js' );
$( function () {
- nav.insertHints( mw.msg( 'prefs-tabs-navigation-hint' ) );
+ var $tabNavigationHint = nav.insertHints( mw.msg( 'prefs-tabs-navigation-hint' ) );
var tabs = OO.ui.infuse( $( '.mw-prefs-tabs' ) );
@@ -136,7 +136,8 @@
} );
}
- var search = OO.ui.infuse( $( '.mw-prefs-search' ) ).fieldWidget;
+ var searchWrapper = OO.ui.infuse( $( '.mw-prefs-search' ) );
+ var search = searchWrapper.fieldWidget;
search.$input.on( 'focus', function () {
if ( !index ) {
// Lazy-build index on first focus
@@ -159,7 +160,7 @@
$( '.mw-prefs-search-matched' ).removeClass( 'mw-prefs-search-matched' );
$( '.mw-prefs-search-highlight' ).removeClass( 'mw-prefs-search-highlight' );
- var hasResults = false;
+ var countResults = 0;
if ( isSearching ) {
val = val.toLowerCase();
texts.forEach( function ( text ) {
@@ -167,20 +168,47 @@
// but might be too slow.
if ( text.indexOf( val ) !== -1 ) {
index[ text ].forEach( function ( item ) {
+ // eslint-disable-next-line no-jquery/no-class-state
+ if ( !item.$field.hasClass( 'mw-prefs-search-matched' ) ) {
+ // Count each matched preference as one result, not the number of matches in the text
+ countResults++;
+ }
item.$highlight.addClass( 'mw-prefs-search-highlight' );
item.$field.addClass( 'mw-prefs-search-matched' );
item.$wrapper.addClass( 'mw-prefs-search-matched' );
item.$tabPanel.addClass( 'mw-prefs-search-matched' );
} );
- hasResults = true;
}
} );
}
- if ( isSearching && !hasResults ) {
+
+ // We hide the tabs when searching, so hide this tip about them as well
+ $tabNavigationHint.toggle( !isSearching );
+ // Update invisible label to give screenreader users live feedback while they're typing
+ if ( !isSearching ) {
+ searchWrapper.setLabel( mw.msg( 'searchprefs' ) );
+ } else if ( countResults === 0 ) {
+ searchWrapper.setLabel( mw.msg( 'searchprefs-noresults' ) );
+ } else {
+ searchWrapper.setLabel( mw.msg( 'searchprefs-results', countResults ) );
+ }
+
+ // Update visible label
+ if ( isSearching && countResults === 0 ) {
tabs.$element.append( $noResults );
} else {
$noResults.detach();
}
+
+ // Make Enter jump to the results, if there are any
+ if ( isSearching && countResults !== 0 ) {
+ search.on( 'enter', function () {
+ tabs.focusFirstFocusable();
+ } );
+ } else {
+ search.off( 'enter' );
+ }
+
} );
// Handle the initial value in case the user started typing before this JS code loaded,
diff --git a/resources/src/mediawiki.widgets.datetime/CalendarWidget.js b/resources/src/mediawiki.widgets.datetime/CalendarWidget.js
index bd152fa306ad..2101ec1118f1 100644
--- a/resources/src/mediawiki.widgets.datetime/CalendarWidget.js
+++ b/resources/src/mediawiki.widgets.datetime/CalendarWidget.js
@@ -17,17 +17,17 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {Object|mw.widgets.datetime.DateTimeFormatter} [formatter={}] Configuration options for
+ * @param {Object|mw.widgets.datetime.DateTimeFormatter} [config.formatter={}] Configuration options for
* mw.widgets.datetime.ProlepticGregorianDateTimeFormatter, or an mw.widgets.datetime.DateTimeFormatter
* instance to use.
- * @cfg {OO.ui.Widget|null} [widget=null] Widget associated with the calendar.
+ * @param {OO.ui.Widget|null} [config.widget=null] Widget associated with the calendar.
* Specifying this configures the calendar to be used as a popup from the
* specified widget (e.g. absolute positioning, automatic hiding when clicked
* outside).
- * @cfg {Date|null} [min=null] Minimum allowed date
- * @cfg {Date|null} [max=null] Maximum allowed date
- * @cfg {Date} [focusedDate] Initially focused date.
- * @cfg {Date|Date[]|null} [selected=null] Selected date(s).
+ * @param {Date|null} [config.min=null] Minimum allowed date
+ * @param {Date|null} [config.max=null] Maximum allowed date
+ * @param {Date} [config.focusedDate] Initially focused date.
+ * @param {Date|Date[]|null} [config.selected=null] Selected date(s).
*/
mw.widgets.datetime.CalendarWidget = function MwWidgetsDatetimeCalendarWidget( config ) {
var $colgroup, $headTR, headings, i;
diff --git a/resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js b/resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js
index d2dc114dfd7f..2ce1a4a8b5c3 100644
--- a/resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js
+++ b/resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js
@@ -10,15 +10,15 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {string} [format='@default'] May be a key from the {@link #static-formats static formats},
+ * @param {string} [config.format='@default'] May be a key from the {@link #static-formats static formats},
* or a format specification as defined by {@link #method-parseFieldSpec parseFieldSpec}
* and {@link #method-getFieldForTag getFieldForTag}.
- * @cfg {boolean} [local=false] Whether dates are local time or UTC
- * @cfg {string[]} [fullZones] Time zone indicators. Array of 2 strings, for
+ * @param {boolean} [config.local=false] Whether dates are local time or UTC
+ * @param {string[]} [config.fullZones] Time zone indicators. Array of 2 strings, for
* UTC and local time.
- * @cfg {string[]} [shortZones] Abbreviated time zone indicators. Array of 2
+ * @param {string[]} [config.shortZones] Abbreviated time zone indicators. Array of 2
* strings, for UTC and local time.
- * @cfg {Date} [defaultDate] Default date, for filling unspecified components.
+ * @param {Date} [config.defaultDate] Default date, for filling unspecified components.
* Defaults to the current date and time (with 0 milliseconds).
*/
mw.widgets.datetime.DateTimeFormatter = function MwWidgetsDatetimeDateTimeFormatter( config ) {
diff --git a/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js b/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js
index 3b85b0bdfe19..32ab5fef2f39 100644
--- a/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js
+++ b/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js
@@ -24,22 +24,22 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {string} [type='datetime'] Whether to act like a 'date', 'time', or 'datetime' input.
+ * @param {string} [config.type='datetime'] Whether to act like a 'date', 'time', or 'datetime' input.
* Affects values stored in the relevant `<input>` and the formatting and
* interpretation of values passed to/from getValue() and setValue(). It's up
* to the user to configure the DateTimeFormatter correctly.
- * @cfg {Object|mw.widgets.datetime.DateTimeFormatter} [formatter={}] Configuration options for
+ * @param {Object|mw.widgets.datetime.DateTimeFormatter} [config.formatter={}] Configuration options for
* mw.widgets.datetime.ProlepticGregorianDateTimeFormatter (with 'format' defaulting to
* '@date', '@time', or '@datetime' depending on 'type'), or an
* mw.widgets.datetime.DateTimeFormatter instance to use.
- * @cfg {Object|null} [calendar={}] Configuration options for
+ * @param {Object|null} [config.calendar={}] Configuration options for
* mw.widgets.datetime.CalendarWidget; note certain settings will be forced based on the
* settings passed to this widget. Set null to disable the calendar.
- * @cfg {boolean} [required=false] Whether a value is required.
- * @cfg {boolean} [clearable=true] Whether to provide for blanking the value.
- * @cfg {Date|null} [value=null] Default value for the widget
- * @cfg {Date|string|null} [min=null] Minimum allowed date
- * @cfg {Date|string|null} [max=null] Maximum allowed date
+ * @param {boolean} [config.required=false] Whether a value is required.
+ * @param {boolean} [config.clearable=true] Whether to provide for blanking the value.
+ * @param {Date|null} [config.value=null] Default value for the widget
+ * @param {Date|string|null} [config.min=null] Minimum allowed date
+ * @param {Date|string|null} [config.max=null] Maximum allowed date
*/
mw.widgets.datetime.DateTimeInputWidget = function MwWidgetsDatetimeDateTimeInputWidget( config ) {
// Configuration initialization
diff --git a/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js b/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js
index 0f5515b9c39f..af3ab1e87a96 100644
--- a/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js
+++ b/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js
@@ -12,20 +12,20 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {Object} [fullMonthNames] Mapping 1–12 to full month names.
- * @cfg {Object} [shortMonthNames] Mapping 1–12 to abbreviated month names.
+ * @param {Object} [config.fullMonthNames] Mapping 1–12 to full month names.
+ * @param {Object} [config.shortMonthNames] Mapping 1–12 to abbreviated month names.
* If {@link #fullMonthNames fullMonthNames} is given and this is not,
* defaults to the first three characters from that setting.
- * @cfg {Object} [fullDayNames] Mapping 0–6 to full day of week names. 0 is Sunday, 6 is Saturday.
- * @cfg {Object} [shortDayNames] Mapping 0–6 to abbreviated day of week names. 0 is Sunday, 6 is Saturday.
+ * @param {Object} [config.fullDayNames] Mapping 0–6 to full day of week names. 0 is Sunday, 6 is Saturday.
+ * @param {Object} [config.shortDayNames] Mapping 0–6 to abbreviated day of week names. 0 is Sunday, 6 is Saturday.
* If {@link #fullDayNames fullDayNames} is given and this is not, defaults to
* the first three characters from that setting.
- * @cfg {string[]} [dayLetters] Weekday column headers for a calendar. Array of 7 strings.
+ * @param {string[]} [config.dayLetters] Weekday column headers for a calendar. Array of 7 strings.
* If {@link #fullDayNames fullDayNames} or {@link #shortDayNames shortDayNames}
* are given and this is not, defaults to the first character from
* shortDayNames.
- * @cfg {string[]} [hour12Periods] AM and PM texts. Array of 2 strings, AM and PM.
- * @cfg {number} [weekStartsOn=0] What day the week starts on: 0 is Sunday, 1 is Monday, 6 is Saturday.
+ * @param {string[]} [config.hour12Periods] AM and PM texts. Array of 2 strings, AM and PM.
+ * @param {number} [config.weekStartsOn=0] What day the week starts on: 0 is Sunday, 1 is Monday, 6 is Saturday.
*/
mw.widgets.datetime.ProlepticGregorianDateTimeFormatter = function MwWidgetsDatetimeProlepticGregorianDateTimeFormatter( config ) {
this.constructor.static.setupDefaults();
diff --git a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.APIResultsProvider.js b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.APIResultsProvider.js
index aab417e2c890..40d3565df8b1 100644
--- a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.APIResultsProvider.js
+++ b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.APIResultsProvider.js
@@ -14,13 +14,13 @@
* @constructor
* @param {string} apiurl The URL to the api
* @param {Object} [config] Configuration options
- * @cfg {number} fetchLimit The default number of results to fetch
- * @cfg {string} lang The language of the API
- * @cfg {number} offset Initial offset, if relevant, to call results from
- * @cfg {Object} ajaxSettings The settings for the ajax call
- * @cfg {Object} staticParams The data parameters that are static and should
+ * @param {number} config.fetchLimit The default number of results to fetch
+ * @param {string} config.lang The language of the API
+ * @param {number} config.offset Initial offset, if relevant, to call results from
+ * @param {Object} config.ajaxSettings The settings for the ajax call
+ * @param {Object} config.staticParams The data parameters that are static and should
* always be sent to the API request, as opposed to user parameters.
- * @cfg {Object} userParams Initial user parameters to be sent as data to
+ * @param {Object} config.userParams Initial user parameters to be sent as data to
* the API request. These can change per request, like the search query term
* or sizing parameters for images, etc.
*/
diff --git a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.APIResultsQueue.js b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.APIResultsQueue.js
index 14618a0e38a3..7ad2683217d3 100644
--- a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.APIResultsQueue.js
+++ b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.APIResultsQueue.js
@@ -13,8 +13,8 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {number} limit The default number of results to fetch
- * @cfg {number} threshold The default number of extra results
+ * @param {number} config.limit The default number of results to fetch
+ * @param {number} config.threshold The default number of extra results
* that the queue should always strive to have on top of the
* individual requests for items.
*/
diff --git a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResourceProvider.js b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResourceProvider.js
index 15e79b999c99..53524455d807 100644
--- a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResourceProvider.js
+++ b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResourceProvider.js
@@ -15,7 +15,7 @@
* @constructor
* @param {string} apiurl The API url
* @param {Object} [config] Configuration options
- * @cfg {string} [scriptDirUrl] The url of the API script
+ * @param {string} [config.scriptDirUrl] The url of the API script
*/
mw.widgets.MediaResourceProvider = function MwWidgetsMediaResourceProvider( apiurl, config ) {
config = config || {};
diff --git a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResourceQueue.js b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResourceQueue.js
index 3131322e6337..ba789ca2ef66 100644
--- a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResourceQueue.js
+++ b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResourceQueue.js
@@ -14,7 +14,7 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {number} maxHeight The maximum height of the media, used in the
+ * @param {number} config.maxHeight The maximum height of the media, used in the
* search call to the API.
*/
mw.widgets.MediaResourceQueue = function MwWidgetsMediaResourceQueue( config ) {
diff --git a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResultWidget.js b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResultWidget.js
index 1c86b11e6f50..40e7b028170f 100644
--- a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResultWidget.js
+++ b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaResultWidget.js
@@ -14,11 +14,11 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {number} [rowHeight] Height of the row this result is part of
- * @cfg {number} [maxRowWidth] A limit for the width of the row this
+ * @param {number} [config.rowHeight] Height of the row this result is part of
+ * @param {number} [config.maxRowWidth] A limit for the width of the row this
* result is a part of.
- * @cfg {number} [minWidth] Minimum width for the result
- * @cfg {number} [maxWidth] Maximum width for the result
+ * @param {number} [config.minWidth] Minimum width for the result
+ * @param {number} [config.maxWidth] Maximum width for the result
*/
mw.widgets.MediaResultWidget = function MwWidgetsMediaResultWidget( config ) {
// Configuration initialization
diff --git a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaSearchQueue.js b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaSearchQueue.js
index 40f05815592a..1d70cc339a1e 100644
--- a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaSearchQueue.js
+++ b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaSearchQueue.js
@@ -14,7 +14,7 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {number} maxHeight The maximum height of the media, used in the
+ * @param {number} config.maxHeight The maximum height of the media, used in the
* search call to the API.
*/
mw.widgets.MediaSearchQueue = function MwWidgetsMediaSearchQueue( config ) {
diff --git a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaUserUploadsQueue.js b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaUserUploadsQueue.js
index 47bf5060a755..6d5da16add03 100644
--- a/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaUserUploadsQueue.js
+++ b/resources/src/mediawiki.widgets/MediaSearch/mw.widgets.MediaUserUploadsQueue.js
@@ -14,7 +14,7 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {number} maxHeight The maximum height of the media, used in the
+ * @param {number} config.maxHeight The maximum height of the media, used in the
* search call to the API.
*/
mw.widgets.MediaUserUploadsQueue = function MwWidgetsMediaUserUploadsQueue( config ) {
diff --git a/resources/src/mediawiki.widgets/Table/mw.widgets.RowWidget.js b/resources/src/mediawiki.widgets/Table/mw.widgets.RowWidget.js
index 9ae31399007b..98f36874ba20 100644
--- a/resources/src/mediawiki.widgets/Table/mw.widgets.RowWidget.js
+++ b/resources/src/mediawiki.widgets/Table/mw.widgets.RowWidget.js
@@ -10,14 +10,14 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {Array} [data] The data of the cells
- * @cfg {Array} [keys] An array of keys for easy cell selection
- * @cfg {RegExp|Function|string} [validate] Validation pattern to apply on every cell
- * @cfg {number} [index] The row index.
- * @cfg {string} [label] The row label to display. If not provided, the row index will
+ * @param {Array} [config.data] The data of the cells
+ * @param {Array} [config.keys] An array of keys for easy cell selection
+ * @param {RegExp|Function|string} [config.validate] Validation pattern to apply on every cell
+ * @param {number} [config.index] The row index.
+ * @param {string} [config.label] The row label to display. If not provided, the row index will
* be used be default. If set to null, no label will be displayed.
- * @cfg {boolean} [showLabel=true] Show row label. Defaults to true.
- * @cfg {boolean} [deletable=true] Whether the table should provide deletion UI tools
+ * @param {boolean} [config.showLabel=true] Show row label. Defaults to true.
+ * @param {boolean} [config.deletable=true] Whether the table should provide deletion UI tools
* for this row or not. Defaults to true.
*/
mw.widgets.RowWidget = function MwWidgetsRowWidget( config ) {
diff --git a/resources/src/mediawiki.widgets/Table/mw.widgets.RowWidgetModel.js b/resources/src/mediawiki.widgets/Table/mw.widgets.RowWidgetModel.js
index 8daf84c8bb18..fabaaf97b8c6 100644
--- a/resources/src/mediawiki.widgets/Table/mw.widgets.RowWidgetModel.js
+++ b/resources/src/mediawiki.widgets/Table/mw.widgets.RowWidgetModel.js
@@ -12,12 +12,12 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {Array} [data] An array containing all values of the row
- * @cfg {Array} [keys] An array of keys for easy cell selection
- * @cfg {RegExp|Function|string} [validate] Validation pattern to apply on every cell
- * @cfg {string} [label=''] Row label. Defaults to empty string.
- * @cfg {boolean} [showLabel=true] Show row label. Defaults to true.
- * @cfg {boolean} [deletable=true] Allow row to be deleted. Defaults to true.
+ * @param {Array} [config.data] An array containing all values of the row
+ * @param {Array} [config.keys] An array of keys for easy cell selection
+ * @param {RegExp|Function|string} [config.validate] Validation pattern to apply on every cell
+ * @param {string} [config.label=''] Row label. Defaults to empty string.
+ * @param {boolean} [config.showLabel=true] Show row label. Defaults to true.
+ * @param {boolean} [config.deletable=true] Allow row to be deleted. Defaults to true.
*/
mw.widgets.RowWidgetModel = function MwWidgetsRowWidgetModel( config ) {
config = config || {};
diff --git a/resources/src/mediawiki.widgets/Table/mw.widgets.TableWidget.js b/resources/src/mediawiki.widgets/Table/mw.widgets.TableWidget.js
index ed72ebae6e91..65ca8eb79230 100644
--- a/resources/src/mediawiki.widgets/Table/mw.widgets.TableWidget.js
+++ b/resources/src/mediawiki.widgets/Table/mw.widgets.TableWidget.js
@@ -7,15 +7,7 @@
* @mixins OO.ui.mixin.GroupElement
*
* @constructor
- * @param {Object} [config] Configuration options
- * @cfg {Array} [rows] An array of objects containing `key` and `label` properties for every row
- * @cfg {Array} [cols] An array of objects containing `key` and `label` properties for every column
- * @cfg {Array} [data] An array containing all values of the table
- * @cfg {RegExp|Function|string} [validate] Validation pattern to apply on every cell
- * @cfg {boolean} [showHeaders=true] Whether or not to show table headers. Defaults to true.
- * @cfg {boolean} [showRowLabels=true] Whether or not to show row labels. Defaults to true.
- * @cfg {boolean} [allowRowInsertion=true] Whether or not to enable row insertion. Defaults to true.
- * @cfg {boolean} [allowRowDeletion=true] Allow row deletion. Defaults to true.
+ * @param {mw.widgets.TableWidgetModel~Config} [config] Configuration options
*/
mw.widgets.TableWidget = function MwWidgetsTableWidget( config ) {
var headerRowItems = [],
diff --git a/resources/src/mediawiki.widgets/Table/mw.widgets.TableWidgetModel.js b/resources/src/mediawiki.widgets/Table/mw.widgets.TableWidgetModel.js
index 8ba7e275e6ce..9da771aa14a6 100644
--- a/resources/src/mediawiki.widgets/Table/mw.widgets.TableWidgetModel.js
+++ b/resources/src/mediawiki.widgets/Table/mw.widgets.TableWidgetModel.js
@@ -5,21 +5,26 @@
*/
/**
+ * Configuration options
+ * @typedef {Object} mw.widgets.TableWidgetModel~Config
+ * @property {Array} [rows] An array of objects containing `key` and `label` properties for every row
+ * @property {Array} [cols] An array of objects containing `key` and `label` properties for every column
+ * @property {Array} [data] An array containing all values of the table
+ * @property {RegExp|Function|string} [validate] Validation pattern to apply on every cell
+ * @property {boolean} [showHeaders=true] Show table header row. Defaults to true.
+ * @property {boolean} [showRowLabels=true] Show row labels. Defaults to true.
+ * @property {boolean} [allowRowInsertion=true] Allow row insertion. Defaults to true.
+ * @property {boolean} [allowRowDeletion=true] Allow row deletion. Defaults to true.
+ */
+
+/**
* TableWidget model.
*
* @class
* @mixins OO.EventEmitter
*
* @constructor
- * @param {Object} [config] Configuration options
- * @cfg {Array} [rows] An array of objects containing `key` and `label` properties for every row
- * @cfg {Array} [cols] An array of objects containing `key` and `label` properties for every column
- * @cfg {Array} [data] An array containing all values of the table
- * @cfg {RegExp|Function|string} [validate] Validation pattern to apply on every cell
- * @cfg {boolean} [showHeaders=true] Show table header row. Defaults to true.
- * @cfg {boolean} [showRowLabels=true] Show row labels. Defaults to true.
- * @cfg {boolean} [allowRowInsertion=true] Allow row insertion. Defaults to true.
- * @cfg {boolean} [allowRowDeletion=true] Allow row deletion. Defaults to true.
+ * @param {mw.widgets.TableWidgetModel~Config} [config] Configuration options
*/
mw.widgets.TableWidgetModel = function MwWidgetsTableWidgetModel( config ) {
config = config || {};
diff --git a/resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.js b/resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.js
index 285cce00c78e..64d86d5708e2 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.CalendarWidget.js
@@ -20,13 +20,13 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {boolean} [lazyInitOnToggle=false] Don't build most of the interface until
+ * @param {boolean} [config.lazyInitOnToggle=false] Don't build most of the interface until
* `.toggle( true )` is called. Meant to be used when the calendar is not immediately visible.
- * @cfg {string} [precision='day'] Date precision to use, 'day' or 'month'
- * @cfg {string|null} [duoDecade='prev'] Alignment of years to display in picker, use 'prev' or 'next'
+ * @param {string} [config.precision='day'] Date precision to use, 'day' or 'month'
+ * @param {string|null} [config.duoDecade='prev'] Alignment of years to display in picker, use 'prev' or 'next'
* 'prev' is previous and current decades
* 'next' is current and next decades
- * @cfg {string|null} [date=null] Day or month date (depending on `precision`), in the format
+ * @param {string|null} [config.date=null] Day or month date (depending on `precision`), in the format
* 'YYYY-MM-DD' or 'YYYY-MM'. When null, the calendar will show today's date, but not select
* it.
*/
diff --git a/resources/src/mediawiki.widgets/mw.widgets.CategoryMultiselectWidget.js b/resources/src/mediawiki.widgets/mw.widgets.CategoryMultiselectWidget.js
index 8e55258a63fe..308ca60bf0f0 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.CategoryMultiselectWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.CategoryMultiselectWidget.js
@@ -33,9 +33,9 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {mw.Api} [api] Instance of mw.Api (or subclass thereof) to use for queries
- * @cfg {number} [limit=10] Maximum number of results to load
- * @cfg {mw.widgets.CategoryMultiselectWidget.SearchType[]} [searchTypes=[mw.widgets.CategoryMultiselectWidget.SearchType.OpenSearch]]
+ * @param {mw.Api} [config.api] Instance of mw.Api (or subclass thereof) to use for queries
+ * @param {number} [config.limit=10] Maximum number of results to load
+ * @param {mw.widgets.CategoryMultiselectWidget.SearchType[]} [config.searchTypes=[mw.widgets.CategoryMultiselectWidget.SearchType.OpenSearch]]
* Default search API to use when searching.
*/
mw.widgets.CategoryMultiselectWidget = function MWCategoryMultiselectWidget( config ) {
diff --git a/resources/src/mediawiki.widgets/mw.widgets.CategoryTagItemWidget.js b/resources/src/mediawiki.widgets/mw.widgets.CategoryTagItemWidget.js
index 4b62c707a92b..6d5aa37442b1 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.CategoryTagItemWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.CategoryTagItemWidget.js
@@ -120,8 +120,8 @@
*
* @constructor
* @param {Object} config Configuration options
- * @cfg {mw.Title} title Page title to use (required)
- * @cfg {string} [apiUrl] API URL, if not the current wiki's API
+ * @param {mw.Title} config.title Page title to use (required)
+ * @param {string} [config.apiUrl] API URL, if not the current wiki's API
*/
mw.widgets.CategoryTagItemWidget = function MWWCategoryTagItemWidget( config ) {
var widget = this;
diff --git a/resources/src/mediawiki.widgets/mw.widgets.CheckMatrixWidget.js b/resources/src/mediawiki.widgets/mw.widgets.CheckMatrixWidget.js
index 2d27b01a4827..f7d4f211980a 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.CheckMatrixWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.CheckMatrixWidget.js
@@ -7,17 +7,17 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {Object} columns Required object mapping column labels (as HTML) to
+ * @param {Object} config.columns Required object mapping column labels (as HTML) to
* their tags.
- * @cfg {Object} rows Required object mapping row labels (as HTML) to their
+ * @param {Object} config.rows Required object mapping row labels (as HTML) to their
* tags.
- * @cfg {string[]} [forcedOn] Array of column-row tags to be displayed as
+ * @param {string[]} [config.forcedOn] Array of column-row tags to be displayed as
* enabled but unavailable to change.
- * @cfg {string[]} [forcedOff] Array of column-row tags to be displayed as
+ * @param {string[]} [config.forcedOff] Array of column-row tags to be displayed as
* disabled but unavailable to change.
- * @cfg {Object} [tooltips] Optional object mapping row labels to tooltips
+ * @param {Object} [config.tooltips] Optional object mapping row labels to tooltips
* (as text, will be escaped).
- * @cfg {Object} [tooltipsHtml] Optional object mapping row labels to tooltips
+ * @param {Object} [config.tooltipsHtml] Optional object mapping row labels to tooltips
* (as HTML). Takes precedence over text tooltips.
*/
mw.widgets.CheckMatrixWidget = function MWWCheckMatrixWidget( config ) {
diff --git a/resources/src/mediawiki.widgets/mw.widgets.ComplexNamespaceInputWidget.js b/resources/src/mediawiki.widgets/mw.widgets.ComplexNamespaceInputWidget.js
index 2d9bfa6c0aea..977a3f5442a1 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.ComplexNamespaceInputWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.ComplexNamespaceInputWidget.js
@@ -15,20 +15,20 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {Object} namespace Configuration for the NamespaceInputWidget dropdown with list
+ * @param {Object} config.namespace Configuration for the NamespaceInputWidget dropdown with list
* of namespaces
- * @cfg {string} namespace.includeAllValue If specified, add a "all namespaces"
+ * @param {string} config.namespace.includeAllValue If specified, add a "all namespaces"
* option to the dropdown, and use this as the input value for it
- * @cfg {Object} invert Configuration for the "invert selection" CheckboxInputWidget. If
+ * @param {Object} config.invert Configuration for the "invert selection" CheckboxInputWidget. If
* null, the checkbox will not be generated.
- * @cfg {Object} associated Configuration for the "include associated namespace"
+ * @param {Object} config.associated Configuration for the "include associated namespace"
* CheckboxInputWidget. If null, the checkbox will not be generated.
- * @cfg {Object} invertLabel Configuration for the FieldLayout with label wrapping the
+ * @param {Object} config.invertLabel Configuration for the FieldLayout with label wrapping the
* "invert selection" checkbox
- * @cfg {string} invertLabel.label Label text for the label
- * @cfg {Object} associatedLabel Configuration for the FieldLayout with label wrapping
+ * @param {string} config.invertLabel.label Label text for the label
+ * @param {Object} config.associatedLabel Configuration for the FieldLayout with label wrapping
* the "include associated namespace" checkbox
- * @cfg {string} associatedLabel.label Label text for the label
+ * @param {string} config.associatedLabel.label Label text for the label
*/
mw.widgets.ComplexNamespaceInputWidget = function MwWidgetsComplexNamespaceInputWidget( config ) {
// Configuration initialization
diff --git a/resources/src/mediawiki.widgets/mw.widgets.ComplexTitleInputWidget.js b/resources/src/mediawiki.widgets/mw.widgets.ComplexTitleInputWidget.js
index 0f39f9ec77a7..26dbcc359b98 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.ComplexTitleInputWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.ComplexTitleInputWidget.js
@@ -14,9 +14,9 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {Object} namespace Configuration for the NamespaceInputWidget dropdown with list of
+ * @param {Object} config.namespace Configuration for the NamespaceInputWidget dropdown with list of
* namespaces
- * @cfg {Object} title Configuration for the TitleInputWidget text field
+ * @param {Object} config.title Configuration for the TitleInputWidget text field
*/
mw.widgets.ComplexTitleInputWidget = function MwWidgetsComplexTitleInputWidget( config ) {
// Parent constructor
diff --git a/resources/src/mediawiki.widgets/mw.widgets.CopyTextLayout.js b/resources/src/mediawiki.widgets/mw.widgets.CopyTextLayout.js
index 9d7d07a3066f..8dfc43efbad3 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.CopyTextLayout.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.CopyTextLayout.js
@@ -13,10 +13,8 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {string} successMessage Success message,
- * defaults to 'mw-widgets-copytextlayout-copy-success'.
- * @cfg {string} failMessage Failure message,
- * defaults to 'mw-widgets-copytextlayout-copy-fail'.
+ * @param {string} [config.successMessage=mw-widgets-copytextlayout-copy-success] Success message
+ * @param {string} [config.failMessage=mw-widgets-copytextlayout-copy-fail] Failure message
*/
mw.widgets.CopyTextLayout = function MwWidgetsCopyTextLayout( config ) {
// Parent constructor
diff --git a/resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js b/resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js
index 9228ccacec04..840432492bea 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js
@@ -61,33 +61,33 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {string} [precision='day'] Date precision to use, 'day' or 'month'
- * @cfg {string} [value] Day or month date (depending on `precision`), in the format 'YYYY-MM-DD'
+ * @param {string} [config.precision='day'] Date precision to use, 'day' or 'month'
+ * @param {string} [config.value] Day or month date (depending on `precision`), in the format 'YYYY-MM-DD'
* or 'YYYY-MM'. If not given or empty string, no date is selected.
- * @cfg {string} [inputFormat] Date format string to use for the textual input field. Displayed
+ * @param {string} [config.inputFormat] Date format string to use for the textual input field. Displayed
* while the widget is active, and the user can type in a date in this format. Should be short
* and easy to type. When not given, defaults to 'YYYY-MM-DD' or 'YYYY-MM', depending on
* `precision`.
- * @cfg {string} [displayFormat] Date format string to use for the clickable label. Displayed
+ * @param {string} [config.displayFormat] Date format string to use for the clickable label. Displayed
* while the widget is inactive. Should be as unambiguous as possible (for example, prefer to
* spell out the month, rather than rely on the order), even if that makes it longer. When not
* given, the default is language-specific.
- * @cfg {boolean} [longDisplayFormat=false] If a custom displayFormat is not specified, use
+ * @param {boolean} [config.longDisplayFormat=false] If a custom displayFormat is not specified, use
* unabbreviated day of the week and month names in the default language-specific displayFormat.
- * @cfg {string} [placeholderLabel=No date selected] Placeholder text shown when the widget is not
+ * @param {string} [config.placeholderLabel=No date selected] Placeholder text shown when the widget is not
* selected. Default text taken from message `mw-widgets-dateinput-no-date`.
- * @cfg {string} [placeholderDateFormat] User-visible date format string displayed in the textual input
+ * @param {string} [config.placeholderDateFormat] User-visible date format string displayed in the textual input
* field when it's empty. Should be the same as `inputFormat`, but translated to the user's
* language. When not given, defaults to a translated version of 'YYYY-MM-DD' or 'YYYY-MM',
* depending on `precision`.
- * @cfg {boolean} [required=false] Mark the field as required. Implies `indicator: 'required'`.
- * @cfg {string} [mustBeAfter] Validates the date to be after this. In the 'YYYY-MM-DD' format.
- * @cfg {string} [mustBeBefore] Validates the date to be before this. In the 'YYYY-MM-DD' format.
- * @cfg {jQuery} [$overlay] Render the calendar into a separate layer. This configuration is
+ * @param {boolean} [config.required=false] Mark the field as required. Implies `indicator: 'required'`.
+ * @param {string} [config.mustBeAfter] Validates the date to be after this. In the 'YYYY-MM-DD' format.
+ * @param {string} [config.mustBeBefore] Validates the date to be before this. In the 'YYYY-MM-DD' format.
+ * @param {jQuery} [config.$overlay] Render the calendar into a separate layer. This configuration is
* useful in cases where the expanded calendar is larger than its container. The specified
* overlay layer is usually on top of the container and has a larger area. By default, the
* calendar uses relative positioning.
- * @cfg {Object} [calendar] Configuration options for the this input's
+ * @param {Object} [config.calendar] Configuration options for the this input's
* {@link mw.widgets.CalendarWidget CalendarWidget}.
*/
mw.widgets.DateInputWidget = function MWWDateInputWidget( config ) {
diff --git a/resources/src/mediawiki.widgets/mw.widgets.NamespaceInputWidget.js b/resources/src/mediawiki.widgets/mw.widgets.NamespaceInputWidget.js
index ee421f298a87..813074ad0f00 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.NamespaceInputWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.NamespaceInputWidget.js
@@ -14,9 +14,9 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {string|null} [includeAllValue] Value for "all namespaces" option, if any
- * @cfg {boolean} [userLang=false] Display namespaces in user language
- * @cfg {number[]} [exclude] List of namespace numbers to exclude from the selector
+ * @param {string|null} [config.includeAllValue] Value for "all namespaces" option, if any
+ * @param {boolean} [config.userLang=false] Display namespaces in user language
+ * @param {number[]} [config.exclude] List of namespace numbers to exclude from the selector
*/
mw.widgets.NamespaceInputWidget = function MwWidgetsNamespaceInputWidget( config ) {
// Configuration initialization
diff --git a/resources/src/mediawiki.widgets/mw.widgets.SearchInputWidget.js b/resources/src/mediawiki.widgets/mw.widgets.SearchInputWidget.js
index 3d463188fb5d..caf9c36d7dd4 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.SearchInputWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.SearchInputWidget.js
@@ -14,10 +14,10 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {boolean} [performSearchOnClick=true] If true, the script will start a search when-
+ * @param {boolean} [config.performSearchOnClick=true] If true, the script will start a search when-
* ever a user hits a suggestion. If false, the text of the suggestion is inserted into the
* text field only.
- * @cfg {string} [dataLocation='header'] Where the search input field will be
+ * @param {string} [config.dataLocation='header'] Where the search input field will be
* used (header or content).
*/
mw.widgets.SearchInputWidget = function MwWidgetsSearchInputWidget( config ) {
diff --git a/resources/src/mediawiki.widgets/mw.widgets.SelectWithInputWidget.js b/resources/src/mediawiki.widgets/mw.widgets.SelectWithInputWidget.js
index 2252aee1e1ea..c68fb52a3033 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.SelectWithInputWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.SelectWithInputWidget.js
@@ -35,11 +35,11 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {Object} [dropdowninput] Config for the dropdown
- * @cfg {Object} [textinput] Config for the text input
- * @cfg {boolean} [or=false] Config for whether the widget is dropdown AND input
+ * @param {Object} [config.dropdowninput] Config for the dropdown
+ * @param {Object} [config.textinput] Config for the text input
+ * @param {boolean} [config.or=false] Config for whether the widget is dropdown AND input
* or dropdown OR input
- * @cfg {boolean} [required=false] Config for whether input is required
+ * @param {boolean} [config.required=false] Config for whether input is required
*/
mw.widgets.SelectWithInputWidget = function MwWidgetsSelectWithInputWidget( config ) {
// Config initialization
diff --git a/resources/src/mediawiki.widgets/mw.widgets.SizeFilterWidget.js b/resources/src/mediawiki.widgets/mw.widgets.SizeFilterWidget.js
index 9cb17704326f..16c9fe806235 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.SizeFilterWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.SizeFilterWidget.js
@@ -22,9 +22,9 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {Object} [radioselectinput] Config for the radio select input
- * @cfg {Object} [textinput] Config for the text input
- * @cfg {boolean} [selectMin=true] Whether to select 'min', false would select 'max'
+ * @param {Object} [config.radioselectinput] Config for the radio select input
+ * @param {Object} [config.textinput] Config for the text input
+ * @param {boolean} [config.selectMin=true] Whether to select 'min', false would select 'max'
*/
mw.widgets.SizeFilterWidget = function MwWidgetsSizeFilterWidget( config ) {
// Config initialization
diff --git a/resources/src/mediawiki.widgets/mw.widgets.TagMultiselectWidget.js b/resources/src/mediawiki.widgets/mw.widgets.TagMultiselectWidget.js
index 74f6f4895302..bc55fa9b1d25 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.TagMultiselectWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.TagMultiselectWidget.js
@@ -20,7 +20,7 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {string} [name] Name of input to submit results (when used in HTML forms)
+ * @param {string} [config.name] Name of input to submit results (when used in HTML forms)
*/
mw.widgets.TagMultiselectWidget = function MwWidgetsTagMultiselectWidget( config ) {
// Parent constructor
diff --git a/resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.js b/resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.js
index af373c55f6c6..b0b98e8e45d1 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.TitleInputWidget.js
@@ -18,8 +18,8 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {boolean} [suggestions=true] Display search suggestions
- * @cfg {RegExp|Function|string} [validate] Perform title validation
+ * @param {boolean} [config.suggestions=true] Display search suggestions
+ * @param {RegExp|Function|string} [config.validate] Perform title validation
*/
mw.widgets.TitleInputWidget = function MwWidgetsTitleInputWidget( config ) {
config = config || {};
diff --git a/resources/src/mediawiki.widgets/mw.widgets.TitleOptionWidget.js b/resources/src/mediawiki.widgets/mw.widgets.TitleOptionWidget.js
index 85a5383f24fe..953ddcaf1dc0 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.TitleOptionWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.TitleOptionWidget.js
@@ -14,16 +14,16 @@
*
* @constructor
* @param {Object} config Configuration options
- * @cfg {string} data Label to display
- * @cfg {string} url URL of page
- * @cfg {boolean} [showImages] Whether to attempt to show images
- * @cfg {string} [imageUrl] Thumbnail image URL with URL encoding
- * @cfg {string} [description] Page description
- * @cfg {boolean} [missing] Page doesn't exist
- * @cfg {boolean} [redirect] Page is a redirect
- * @cfg {boolean} [disambiguation] Page is a disambiguation page
- * @cfg {string} [query] Matching query string to highlight
- * @cfg {Function} [compare] String comparison function for query highlighting
+ * @param {string} config.data Label to display
+ * @param {string} config.url URL of page
+ * @param {boolean} [config.showImages] Whether to attempt to show images
+ * @param {string} [config.imageUrl] Thumbnail image URL with URL encoding
+ * @param {string} [config.description] Page description
+ * @param {boolean} [config.missing] Page doesn't exist
+ * @param {boolean} [config.redirect] Page is a redirect
+ * @param {boolean} [config.disambiguation] Page is a disambiguation page
+ * @param {string} [config.query] Matching query string to highlight
+ * @param {Function} [config.compare] String comparison function for query highlighting
*/
mw.widgets.TitleOptionWidget = function MwWidgetsTitleOptionWidget( config ) {
var icon;
diff --git a/resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js b/resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js
index c75e9f077c6c..5f52067f8f1e 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js
@@ -15,28 +15,28 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {number} [limit=10] Number of results to show
- * @cfg {number} [namespace] Namespace to prepend to queries
- * @cfg {number} [maxLength=255] Maximum query length
- * @cfg {boolean} [relative=true] If a namespace is set, display titles relative to it
- * @cfg {boolean} [suggestions=true] Display search suggestions
- * @cfg {boolean} [showRedirectTargets=true] Show the targets of redirects
- * @cfg {boolean} [showImages=false] Show page images
- * @cfg {boolean} [showDescriptions=false] Show page descriptions
- * @cfg {boolean} [showDisambigsLast=false] Show disambiguation pages as the last results
- * @cfg {boolean} [showMissing] Show the user's input as a missing page when a page with this
+ * @param {number} [config.limit=10] Number of results to show
+ * @param {number} [config.namespace] Namespace to prepend to queries
+ * @param {number} [config.maxLength=255] Maximum query length
+ * @param {boolean} [config.relative=true] If a namespace is set, display titles relative to it
+ * @param {boolean} [config.suggestions=true] Display search suggestions
+ * @param {boolean} [config.showRedirectTargets=true] Show the targets of redirects
+ * @param {boolean} [config.showImages=false] Show page images
+ * @param {boolean} [config.showDescriptions=false] Show page descriptions
+ * @param {boolean} [config.showDisambigsLast=false] Show disambiguation pages as the last results
+ * @param {boolean} [config.showMissing] Show the user's input as a missing page when a page with this
* exact name doesn't exist. Disabled by default when the namespace option is used, otherwise
* enabled by default.
- * @cfg {boolean} [showInterwikis=false] Show pages with a valid interwiki prefix
- * @cfg {boolean} [searchFragments=false] Search for hash fragments on a specific page when typed
- * @cfg {boolean} [addQueryInput=true] Add exact user's input query to results
- * @cfg {boolean} [excludeCurrentPage=false] Exclude the current page from suggestions
- * @cfg {boolean} [excludeDynamicNamespaces=false] Exclude pages whose namespace is negative
- * @cfg {boolean} [validateTitle=true] Whether the input must be a valid title
- * @cfg {boolean} [required=false] Whether the input must not be empty
- * @cfg {boolean} [highlightSearchQuery=true] Highlight the partial query the user used for this title
- * @cfg {Object} [cache] Result cache which implements a 'set' method, taking keyed values as an argument
- * @cfg {mw.Api} [api] API object to use, creates a default mw.Api instance if not specified
+ * @param {boolean} [config.showInterwikis=false] Show pages with a valid interwiki prefix
+ * @param {boolean} [config.searchFragments=false] Search for hash fragments on a specific page when typed
+ * @param {boolean} [config.addQueryInput=true] Add exact user's input query to results
+ * @param {boolean} [config.excludeCurrentPage=false] Exclude the current page from suggestions
+ * @param {boolean} [config.excludeDynamicNamespaces=false] Exclude pages whose namespace is negative
+ * @param {boolean} [config.validateTitle=true] Whether the input must be a valid title
+ * @param {boolean} [config.required=false] Whether the input must not be empty
+ * @param {boolean} [config.highlightSearchQuery=true] Highlight the partial query the user used for this title
+ * @param {Object} [config.cache] Result cache which implements a 'set' method, taking keyed values as an argument
+ * @param {mw.Api} [config.api] API object to use, creates a default mw.Api instance if not specified
*/
mw.widgets.TitleWidget = function MwWidgetsTitleWidget( config ) {
// Config initialization
diff --git a/resources/src/mediawiki.widgets/mw.widgets.ToggleSwitchWidget.js b/resources/src/mediawiki.widgets/mw.widgets.ToggleSwitchWidget.js
index 0171db4b66bf..59e94600c4e3 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.ToggleSwitchWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.ToggleSwitchWidget.js
@@ -14,7 +14,7 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {string} [name] Name of input to submit results (when used in HTML forms)
+ * @param {string} [config.name] Name of input to submit results (when used in HTML forms)
*/
mw.widgets.ToggleSwitchWidget = function MwWidgetsToggleWidget( config ) {
// Parent constructor
diff --git a/resources/src/mediawiki.widgets/mw.widgets.UserInputWidget.js b/resources/src/mediawiki.widgets/mw.widgets.UserInputWidget.js
index efa09d78f62d..08a63d7d3285 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.UserInputWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.UserInputWidget.js
@@ -15,8 +15,8 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {number} [limit=10] Number of results to show
- * @cfg {mw.Api} [api] API object to use, creates a default mw.Api instance if not specified
+ * @param {number} [config.limit=10] Number of results to show
+ * @param {mw.Api} [config.api] API object to use, creates a default mw.Api instance if not specified
*/
mw.widgets.UserInputWidget = function MwWidgetsUserInputWidget( config ) {
// Config initialization
diff --git a/resources/src/mediawiki.widgets/mw.widgets.UsersMultiselectWidget.js b/resources/src/mediawiki.widgets/mw.widgets.UsersMultiselectWidget.js
index 810725dfc3db..aaefa8440079 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.UsersMultiselectWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.UsersMultiselectWidget.js
@@ -21,15 +21,15 @@
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {mw.Api} [api] Instance of mw.Api (or subclass thereof) to use for queries
- * @cfg {number} [limit=10] Number of results to show in autocomplete menu
- * @cfg {string} [name] Name of input to submit results (when used in HTML forms)
- * @cfg {boolean} [ipAllowed=false] Show IP addresses in autocomplete menu
+ * @param {mw.Api} [config.api] Instance of mw.Api (or subclass thereof) to use for queries
+ * @param {number} [config.imit=10] Number of results to show in autocomplete menu
+ * @param {string} [config.name] Name of input to submit results (when used in HTML forms)
+ * @param {boolean} [config.ipAllowed=false] Show IP addresses in autocomplete menu
* If false, single IP addresses are not allowed, even if IP ranges are allowed.
- * @cfg {boolean} [ipRangeAllowed=false] Show IP ranges in autocomplete menu
- * @cfg {Object} [ipRangeLimits] Maximum allowed IP ranges (defaults match HTMLUserTextField.php)
- * @cfg {number} [ipRangeLimits.IPv4 = 16] Maximum allowed IPv4 range
- * @cfg {number} [ipRangeLimits.IPv6 = 32] Maximum allowed IPv6 range
+ * @param {boolean} [config.ipRangeAllowed=false] Show IP ranges in autocomplete menu
+ * @param {Object} [config.ipRangeLimits] Maximum allowed IP ranges (defaults match HTMLUserTextField.php)
+ * @param {number} [config.ipRangeLimits.IPv4 = 16] Maximum allowed IPv4 range
+ * @param {number} [config.ipRangeLimits.IPv6 = 32] Maximum allowed IPv6 range
*/
mw.widgets.UsersMultiselectWidget = function MwWidgetsUsersMultiselectWidget( config ) {
// Config initialization
diff --git a/resources/src/startup/mediawiki.loader.js b/resources/src/startup/mediawiki.loader.js
index cfb4708ab547..6f229b9a3d6a 100644
--- a/resources/src/startup/mediawiki.loader.js
+++ b/resources/src/startup/mediawiki.loader.js
@@ -184,8 +184,9 @@
/**
* Create a new style element and add it to the DOM.
+ * Stable for use in gadgets.
*
- * @private
+ * @name mw.loader.addStyleTag
* @param {string} text CSS text
* @param {Node|null} [nextNode] The element where the style tag
* should be inserted before
@@ -1350,10 +1351,13 @@
// Exposed for internal use only. Documented as @private.
addScriptTag: addScript,
+ // Exposed for internal use only. Documented as @private.
addLinkTag: addLink,
+ // Exposed for internal use only. Documented as @private.
enqueue: enqueue,
+ // Exposed for internal use only. Documented as @private.
resolve: resolve,
/**
@@ -2131,5 +2135,4 @@
}
}
};
-
}() );
diff --git a/tests/parser/ParserTestRunner.php b/tests/parser/ParserTestRunner.php
index 100f610433ab..cde706fd4c3a 100644
--- a/tests/parser/ParserTestRunner.php
+++ b/tests/parser/ParserTestRunner.php
@@ -1616,6 +1616,9 @@ class ParserTestRunner {
$after[] = $tocData->prettyPrint();
}
}
+ if ( isset( $opts['showmedia'] ) ) {
+ $after[] = 'images=' . implode( ', ', array_keys( $output->getImages() ) );
+ }
if ( $metadataExpected === null ) {
// legacy format, add $before and $after to $out
if ( $before ) {
diff --git a/tests/parser/parserTests.txt b/tests/parser/parserTests.txt
index ce20d1c2232a..f5b8bba60722 100644
--- a/tests/parser/parserTests.txt
+++ b/tests/parser/parserTests.txt
@@ -12064,6 +12064,21 @@ formatdate uses correct capitalisation in English
<p><span class="mw-formatted-date" title="06-03" about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"stx":"html","pi":[[{"k":"1"}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"#formatdate:june 3","function":"formatdate"},"params":{"1":{"wt":"dmy"}},"i":0}}]}'>3 June</span></p>
!! end
+!! test
+formatdate formats ISO year 0 as 1 BC
+!! wikitext
+{{#formatdate:-0001-12-31|mdy}}
+{{#formatdate:0000-12-31|mdy}}
+{{#formatdate:0001-12-31|mdy}}
+!! html/php
+<p><span class="mw-formatted-date" title="-0001-12-31">December 31, 2 BC</span>
+<span class="mw-formatted-date" title="0000-12-31">December 31, 1 BC</span>
+<span class="mw-formatted-date" title="0001-12-31">December 31, 1</span>
+</p>
+!! html/parsoid+integrated
+<p><span class="mw-formatted-date" title="-0001-12-31" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"#formatdate:-0001-12-31","function":"formatdate"},"params":{"1":{"wt":"mdy"}},"i":0}}]}'>December 31, 2 BC</span> <span class="mw-formatted-date" title="0000-12-31" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"#formatdate:0000-12-31","function":"formatdate"},"params":{"1":{"wt":"mdy"}},"i":0}}]}'>December 31, 1 BC</span> <span class="mw-formatted-date" title="0001-12-31" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"#formatdate:0001-12-31","function":"formatdate"},"params":{"1":{"wt":"mdy"}},"i":0}}]}'>December 31, 1</span></p>
+!! end
+
#
#
#
diff --git a/tests/phpunit/MediaWikiTestCaseTrait.php b/tests/phpunit/MediaWikiTestCaseTrait.php
index 1c37ccd3495e..28ba64a0795e 100644
--- a/tests/phpunit/MediaWikiTestCaseTrait.php
+++ b/tests/phpunit/MediaWikiTestCaseTrait.php
@@ -455,30 +455,6 @@ trait MediaWikiTestCaseTrait {
}
/**
- * Forward-compatibility method to replace assertObjectHasAttribute in PHPUnit 9. This can be removed when
- * upgrading to PHPUnit 10, which introduces this method upstream.
- */
- protected function assertObjectHasProperty( string $propertyName, object $object, string $message = '' ): void {
- $this->assertTrue(
- ( new ReflectionObject( $object ) )->hasProperty( $propertyName ),
- $message ?: 'Failed asserting that object of class "' . get_class( $object ) .
- "\" has property \"$propertyName\""
- );
- }
-
- /**
- * Forward-compatibility method to replace assertObjectNotHasAttribute in PHPUnit 9. This can be removed when
- * upgrading to PHPUnit 10, which introduces this method upstream.
- */
- protected function assertObjectNotHasProperty( string $propertyName, object $object, string $message = '' ): void {
- $this->assertFalse(
- ( new ReflectionObject( $object ) )->hasProperty( $propertyName ),
- $message ?: 'Failed asserting that object of class "' . get_class( $object ) .
- "\" does not have property \"$propertyName\""
- );
- }
-
- /**
* This method allows you to assert that the given callback emits a PHP error. It is a PHPUnit 10 compatible
* replacement for expectNotice(), expectWarning(), expectError(), and the other methods deprecated in
* https://github.com/sebastianbergmann/phpunit/issues/5062.
diff --git a/tests/phpunit/bootstrap.php b/tests/phpunit/bootstrap.php
index 047a9cddf943..8b6b3f8b573e 100644
--- a/tests/phpunit/bootstrap.php
+++ b/tests/phpunit/bootstrap.php
@@ -46,7 +46,9 @@ if ( $GLOBALS['argc'] === 1 ) {
} else {
// PHPUnit has been invoked with arguments. This can be very complex to handle, so the heuristic below is meant
// to cover just the most common use cases.
- $phpunitArgs = ( new Builder )->fromParameters( $GLOBALS['argv'], [] );
+ // Make PHPUnit not complain about unrecognized options when paratest options are passed in
+ $paratestArgs = [ 'runner', 'processes', 'passthru-php', 'write-to' ];
+ $phpunitArgs = ( new Builder )->fromParameters( $GLOBALS['argv'], $paratestArgs );
if ( $phpunitArgs->hasArgument() ) {
// A test or test directory was specified explicitly. Normalize line endings and case, and see if we likely
// got a directory of unit tests only (or a file therein).
diff --git a/tests/phpunit/includes/Output/OutputPageTest.php b/tests/phpunit/includes/Output/OutputPageTest.php
index fa963a0d330c..db8ce0810c08 100644
--- a/tests/phpunit/includes/Output/OutputPageTest.php
+++ b/tests/phpunit/includes/Output/OutputPageTest.php
@@ -3214,7 +3214,7 @@ class OutputPageTest extends MediaWikiIntegrationTestCase {
[ 'sr-ec', [ NS_FILE, 'Example' ], 'sr', 'sr', 'sr-ec' ],
[ 'sr', [ NS_FILE, 'Example' ], 'sr', 'sr', 'sr' ],
[ 'sr-ec', [ NS_MEDIAWIKI, 'Example' ], 'sr-ec', 'sr-ec', 'sr' ],
- [ 'sr' , [ NS_MEDIAWIKI, 'Example' ], 'sr', 'sr', 'sr-ec' ],
+ [ 'sr', [ NS_MEDIAWIKI, 'Example' ], 'sr', 'sr', 'sr-ec' ],
];
}
diff --git a/tests/phpunit/includes/OutputTransform/OutputTransformStageTestBase.php b/tests/phpunit/includes/OutputTransform/OutputTransformStageTestBase.php
index 30e6621b1e1f..eda17cc0c8d3 100644
--- a/tests/phpunit/includes/OutputTransform/OutputTransformStageTestBase.php
+++ b/tests/phpunit/includes/OutputTransform/OutputTransformStageTestBase.php
@@ -57,6 +57,6 @@ abstract class OutputTransformStageTestBase extends MediaWikiIntegrationTestCase
$key = PageBundleParserOutputConverter::PARSOID_PAGE_BUNDLE_KEY;
$expected->setExtensionData( $key, $result->getExtensionData( $key ) );
}
- $this->assertEquals( $expected, $result );
+ $this->assertEquals( $expected, $result );
}
}
diff --git a/tests/phpunit/includes/api/query/ApiQueryUserInfoTest.php b/tests/phpunit/includes/api/query/ApiQueryUserInfoTest.php
index 701e39f275a2..987fb8266d10 100644
--- a/tests/phpunit/includes/api/query/ApiQueryUserInfoTest.php
+++ b/tests/phpunit/includes/api/query/ApiQueryUserInfoTest.php
@@ -3,6 +3,8 @@
namespace MediaWiki\Tests\Api\Query;
use MediaWiki\Tests\Api\ApiTestCase;
+use MediaWiki\Tests\Unit\Permissions\MockAuthorityTrait;
+use MediaWiki\Tests\User\TempUser\TempUserTestTrait;
use MediaWiki\Utils\MWTimestamp;
/**
@@ -13,6 +15,9 @@ use MediaWiki\Utils\MWTimestamp;
*/
class ApiQueryUserInfoTest extends ApiTestCase {
+ use TempUserTestTrait;
+ use MockAuthorityTrait;
+
/**
* @covers \ApiQueryUserInfo::getLatestContributionTime
*/
@@ -85,4 +90,47 @@ class ApiQueryUserInfoTest extends ApiTestCase {
$this->assertFalse( $apiResult[0]['query']['userinfo']['cancreateaccount'] );
$this->assertArrayHasKey( 'cancreateaccounterror', $apiResult[0]['query']['userinfo'] );
}
+
+ public function testTempFlag() {
+ $this->enableAutoCreateTempUser();
+ $params = [
+ 'action' => 'query',
+ 'meta' => 'userinfo',
+ ];
+ $user = $this->getServiceContainer()->getTempUserCreator()->create()->getUser();
+ $apiResult = $this->doApiRequest( $params, null, false, $user );
+
+ // Verify that the temp flag is set.
+ $this->assertArrayHasKey( 'query', $apiResult[0] );
+ $this->assertArrayHasKey( 'userinfo', $apiResult[0]['query'] );
+ $this->assertArrayHasKey( 'temp', $apiResult[0]['query']['userinfo'] );
+ $this->assertTrue( $apiResult[0]['query']['userinfo']['temp'] );
+
+ // Verify that the name is correct
+ $this->assertArrayHasKey( 'name', $apiResult[0]['query']['userinfo'] );
+ $this->assertSame( $user->getName(), $apiResult[0]['query']['userinfo']['name'] );
+
+ // Verify that the user ID is correct
+ $this->assertArrayHasKey( 'id', $apiResult[0]['query']['userinfo'] );
+ $this->assertSame( $user->getId(), $apiResult[0]['query']['userinfo']['id'] );
+ }
+
+ public function testAnonFlag() {
+ $this->disableAutoCreateTempUser();
+ $params = [
+ 'action' => 'query',
+ 'meta' => 'userinfo',
+ ];
+ $user = $this->mockAnonUltimateAuthority();
+ $apiResult = $this->doApiRequest( $params, null, false, $user );
+
+ // Verify that the temp flag is not set.
+ $this->assertArrayHasKey( 'query', $apiResult[0] );
+ $this->assertArrayHasKey( 'userinfo', $apiResult[0]['query'] );
+ $this->assertArrayNotHasKey( 'temp', $apiResult[0]['query']['userinfo'] );
+
+ // Verify that the anon flag is set.
+ $this->assertArrayHasKey( 'anon', $apiResult[0]['query']['userinfo'] );
+ $this->assertTrue( $apiResult[0]['query']['userinfo']['anon'] );
+ }
}
diff --git a/tests/phpunit/includes/cache/GenderCacheTest.php b/tests/phpunit/includes/cache/GenderCacheTest.php
index d12115608d9a..746298cd4504 100644
--- a/tests/phpunit/includes/cache/GenderCacheTest.php
+++ b/tests/phpunit/includes/cache/GenderCacheTest.php
@@ -1,5 +1,7 @@
<?php
+use MediaWiki\Cache\GenderCache;
+
/**
* @group Database
* @group Cache
diff --git a/tests/phpunit/includes/cache/LinkBatchTest.php b/tests/phpunit/includes/cache/LinkBatchTest.php
index 65b3f5bf74ce..8c6f89f3270d 100644
--- a/tests/phpunit/includes/cache/LinkBatchTest.php
+++ b/tests/phpunit/includes/cache/LinkBatchTest.php
@@ -1,6 +1,9 @@
<?php
use MediaWiki\Cache\CacheKeyHelper;
+use MediaWiki\Cache\GenderCache;
+use MediaWiki\Cache\LinkBatch;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Linker\LinksMigration;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\Logger\LoggerFactory;
diff --git a/tests/phpunit/includes/cache/LinkCacheTest.php b/tests/phpunit/includes/cache/LinkCacheTest.php
index 68bba5e6b062..b0684ef8dc2b 100644
--- a/tests/phpunit/includes/cache/LinkCacheTest.php
+++ b/tests/phpunit/includes/cache/LinkCacheTest.php
@@ -1,5 +1,6 @@
<?php
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Page\PageReference;
use MediaWiki\Page\PageReferenceValue;
use MediaWiki\Title\Title;
diff --git a/tests/phpunit/includes/filebackend/FileBackendIntegrationTest.php b/tests/phpunit/includes/filebackend/FileBackendIntegrationTest.php
index c292a8dd0c9f..ebb7659da5a4 100644
--- a/tests/phpunit/includes/filebackend/FileBackendIntegrationTest.php
+++ b/tests/phpunit/includes/filebackend/FileBackendIntegrationTest.php
@@ -1672,19 +1672,19 @@ class FileBackendIntegrationTest extends MediaWikiIntegrationTestCase {
}
}
- public function testDoOperations() {
+ public function testDoOperationsSuccessful() {
$this->backend = $this->singleBackend;
$this->tearDownFiles();
- $this->doTestDoOperations();
+ $this->doTestDoOperationsSuccessful();
$this->tearDownFiles();
$this->backend = $this->multiBackend;
$this->tearDownFiles();
- $this->doTestDoOperations();
+ $this->doTestDoOperationsSuccessful();
$this->tearDownFiles();
}
- private function doTestDoOperations() {
+ private function doTestDoOperationsSuccessful() {
$base = self::baseStorePath();
$fileA = "$base/unittest-cont1/e/a/b/fileA.txt";
diff --git a/tests/phpunit/includes/linker/LinkRendererTest.php b/tests/phpunit/includes/linker/LinkRendererTest.php
index 88fb4b91e42b..681a8cb24744 100644
--- a/tests/phpunit/includes/linker/LinkRendererTest.php
+++ b/tests/phpunit/includes/linker/LinkRendererTest.php
@@ -1,5 +1,6 @@
<?php
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Linker\LinkRenderer;
use MediaWiki\Linker\LinkRendererFactory;
diff --git a/tests/phpunit/includes/logging/LogFormatterTestCase.php b/tests/phpunit/includes/logging/LogFormatterTestCase.php
index 065cd2902b55..ce8c0ddd7893 100644
--- a/tests/phpunit/includes/logging/LogFormatterTestCase.php
+++ b/tests/phpunit/includes/logging/LogFormatterTestCase.php
@@ -1,5 +1,7 @@
<?php
+use MediaWiki\Cache\GenderCache;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Context\RequestContext;
use MediaWiki\Linker\LinkRenderer;
diff --git a/tests/phpunit/includes/page/ParserOutputAccessTest.php b/tests/phpunit/includes/page/ParserOutputAccessTest.php
index 23029dc6bbef..a0f0884f8869 100644
--- a/tests/phpunit/includes/page/ParserOutputAccessTest.php
+++ b/tests/phpunit/includes/page/ParserOutputAccessTest.php
@@ -3,12 +3,16 @@ use MediaWiki\Json\JsonCodec;
use MediaWiki\Logger\Spi as LoggerSpi;
use MediaWiki\MainConfigNames;
use MediaWiki\Page\Hook\OpportunisticLinksUpdateHook;
+use MediaWiki\Page\PageRecord;
use MediaWiki\Page\ParserOutputAccess;
+use MediaWiki\Page\WikiPageFactory;
use MediaWiki\Parser\ParserCacheFactory;
use MediaWiki\Parser\RevisionOutputCache;
use MediaWiki\PoolCounter\PoolCounter;
use MediaWiki\PoolCounter\PoolCounterFactory;
+use MediaWiki\PoolCounter\PoolCounterWork;
use MediaWiki\Revision\MutableRevisionRecord;
+use MediaWiki\Revision\RevisionLookup;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Revision\RevisionRenderer;
use MediaWiki\Revision\RevisionStore;
@@ -18,6 +22,8 @@ use MediaWiki\Utils\MWTimestamp;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
+use Wikimedia\Rdbms\ChronologyProtector;
+use Wikimedia\Rdbms\ILBFactory;
use Wikimedia\TestingAccessWrapper;
/**
@@ -26,6 +32,19 @@ use Wikimedia\TestingAccessWrapper;
*/
class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
+ public int $actualCallsToPoolWorkArticleView = 0;
+ public int $expectedCallsToPoolWorkArticleView = 0;
+
+ public function tearDown(): void {
+ $this->assertSame(
+ $this->expectedCallsToPoolWorkArticleView,
+ $this->actualCallsToPoolWorkArticleView,
+ 'Calls to newPoolWorkArticleView'
+ );
+
+ parent::tearDown();
+ }
+
private function getHtml( $value ) {
if ( $value instanceof StatusValue ) {
$value = $value->getValue();
@@ -128,39 +147,53 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
$parserCache = null,
$revisionOutputCache = null,
$maxRenderCalls = false
- ) {
- if ( !$parserCache ) {
- $parserCache = $this->getParserCache( new HashBagOStuff() );
- }
-
- if ( !$revisionOutputCache ) {
- $revisionOutputCache = $this->getRevisionOutputCache( new HashBagOStuff() );
- }
-
- $parserCacheFactory = $this->createMock( ParserCacheFactory::class );
- $parserCacheFactory->method( 'getParserCache' )->willReturn( $parserCache );
- $parserCacheFactory->method( 'getRevisionOutputCache' )->willReturn( $revisionOutputCache );
- return $this->getParserOutputAccessWithCacheFactory(
- $parserCacheFactory,
- $maxRenderCalls
- );
+ ): ParserOutputAccess {
+ return $this->getParserOutputAccess( [
+ 'parserCache' => $parserCache ?? new HashBagOStuff(),
+ 'revisionOutputCache' => $revisionOutputCache ?? new HashBagOStuff(),
+ 'maxRenderCalls' => $maxRenderCalls
+ ] );
}
/**
- * @param ParserCacheFactory $parserCacheFactory
- * @param int|bool $maxRenderCalls
+ * @param array $options
*
* @return ParserOutputAccess
* @throws Exception
*/
- private function getParserOutputAccessWithCacheFactory(
- $parserCacheFactory,
- $maxRenderCalls = false
- ) {
- $revRenderer = $this->getServiceContainer()->getRevisionRenderer();
+ private function getParserOutputAccess( array $options = [] ): ParserOutputAccess {
+ $parserCacheFactory = $options['parserCacheFactory'] ?? null;
+ $maxRenderCalls = $options['maxRenderCalls'] ?? null;
+ $parserCache = $options['parserCache'] ?? null;
+ $revisionOutputCache = $options['revisionOutputCache'] ?? null;
+ $expectPoolCounterCalls = $options['expectPoolCounterCalls'] ?? 0;
+
+ if ( !$parserCacheFactory ) {
+ if ( !$parserCache instanceof ParserCache ) {
+ $parserCache = $this->getParserCache(
+ $parserCache ?? new EmptyBagOStuff()
+ );
+ }
+
+ if ( !$revisionOutputCache instanceof RevisionOutputCache ) {
+ $revisionOutputCache = $this->getRevisionOutputCache(
+ $revisionOutputCache ?? new EmptyBagOStuff()
+ );
+ }
+
+ $parserCacheFactory = $this->createMock( ParserCacheFactory::class );
+
+ $parserCacheFactory->method( 'getParserCache' )
+ ->willReturn( $parserCache );
+
+ $parserCacheFactory->method( 'getRevisionOutputCache' )
+ ->willReturn( $revisionOutputCache );
+ }
+ $revRenderer = $this->getServiceContainer()->getRevisionRenderer();
if ( $maxRenderCalls ) {
$realRevRenderer = $revRenderer;
+
$revRenderer =
$this->createNoOpMock( RevisionRenderer::class, [ 'getRenderedRevision' ] );
@@ -169,27 +202,61 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
->willReturnCallback( [ $realRevRenderer, 'getRenderedRevision' ] );
}
- return new ParserOutputAccess(
- $parserCacheFactory,
- $this->getServiceContainer()->getRevisionLookup(),
- $revRenderer,
- new NullStatsdDataFactory(),
- $this->getServiceContainer()->getConnectionProvider(),
- $this->getServiceContainer()->getChronologyProtector(),
- $this->getLoggerSpi(),
- $this->getServiceContainer()->getWikiPageFactory(),
- $this->getServiceContainer()->getTitleFormatter()
- );
- }
-
- /**
- * @return ParserOutputAccess
- */
- private function getParserOutputAccessNoCache() {
- return $this->getParserOutputAccessWithCache(
- $this->getParserCache( new EmptyBagOStuff() ),
- $this->getRevisionOutputCache( new EmptyBagOStuff() )
- );
+ $mock = new class (
+ $parserCacheFactory,
+ $this->getServiceContainer()->getRevisionLookup(),
+ $revRenderer,
+ new NullStatsdDataFactory(),
+ $this->getServiceContainer()->getDBLoadBalancerFactory(),
+ $this->getServiceContainer()->getChronologyProtector(),
+ $this->getLoggerSpi(),
+ $this->getServiceContainer()->getWikiPageFactory(),
+ $this->getServiceContainer()->getTitleFormatter(),
+ $this
+ ) extends ParserOutputAccess {
+ private ParserOutputAccessTest $test;
+
+ public function __construct(
+ ParserCacheFactory $parserCacheFactory,
+ RevisionLookup $revisionLookup,
+ RevisionRenderer $revisionRenderer,
+ IBufferingStatsdDataFactory $statsDataFactory,
+ ILBFactory $lbFactory,
+ ChronologyProtector $chronologyProtector,
+ LoggerSpi $loggerSpi,
+ WikiPageFactory $wikiPageFactory,
+ TitleFormatter $titleFormatter,
+ ParserOutputAccessTest $test
+ ) {
+ parent::__construct(
+ $parserCacheFactory,
+ $revisionLookup,
+ $revisionRenderer,
+ $statsDataFactory,
+ $lbFactory,
+ $chronologyProtector,
+ $loggerSpi,
+ $wikiPageFactory,
+ $titleFormatter
+ );
+
+ $this->test = $test;
+ }
+
+ protected function newPoolWorkArticleView(
+ PageRecord $page,
+ ParserOptions $parserOptions,
+ RevisionRecord $revision,
+ int $options
+ ): PoolCounterWork {
+ $this->test->actualCallsToPoolWorkArticleView++;
+ return parent::newPoolWorkArticleView( $page, $parserOptions, $revision, $options );
+ }
+ };
+
+ $this->expectedCallsToPoolWorkArticleView += $expectPoolCounterCalls;
+
+ return $mock;
}
/**
@@ -236,7 +303,9 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
* Tests that we can get rendered output for the latest revision.
*/
public function testOutputForLatestRevision() {
- $access = $this->getParserOutputAccessNoCache();
+ $access = $this->getParserOutputAccess( [
+ 'parserCache' => new HashBagOStuff()
+ ] );
$page = $this->getNonexistingTestPage( __METHOD__ );
$this->editPage( $page, 'Hello \'\'World\'\'!' );
@@ -246,13 +315,40 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
$this->installOpportunisticUpdateHook( false );
$status = $access->getParserOutput( $page, $parserOptions );
$this->assertContainsHtml( 'Hello <i>World</i>!', $status );
+
+ $this->assertNotNull( $access->getCachedParserOutput( $page, $parserOptions ) );
+ }
+
+ /**
+ * Tests that we can get rendered output for the latest revision.
+ */
+ public function testOutputForLatestRevisionUsingPoolCounter() {
+ $access = $this->getParserOutputAccess( [
+ 'expectPoolCounterCalls' => 1
+ ] );
+
+ $page = $this->getNonexistingTestPage( __METHOD__ );
+ $this->editPage( $page, 'Hello \'\'World\'\'!' );
+
+ $parserOptions = $this->getParserOptions();
+
+ // WikiPage::triggerOpportunisticLinksUpdate is not called by default
+ $this->installOpportunisticUpdateHook( false );
+
+ $status = $access->getParserOutput(
+ $page,
+ $parserOptions,
+ null,
+ ParserOutputAccess::OPT_FOR_ARTICLE_VIEW
+ );
+ $this->assertContainsHtml( 'Hello <i>World</i>!', $status );
}
/**
* Tests that we can get rendered output for the latest revision.
*/
public function testOutputForLatestRevisionWithLinksUpdate() {
- $access = $this->getParserOutputAccessNoCache();
+ $access = $this->getParserOutputAccess();
$page = $this->getNonexistingTestPage( __METHOD__ );
$this->editPage( $page, 'Hello \'\'World\'\'!' );
@@ -265,6 +361,30 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
}
/**
+ * Tests that we can get rendered output for the latest revision.
+ */
+ public function testOutputForLatestRevisionWithLinksUpdateWithPoolCounter() {
+ $access = $this->getParserOutputAccess( [
+ 'expectPoolCounterCalls' => 1
+ ] );
+
+ $page = $this->getNonexistingTestPage( __METHOD__ );
+ $this->editPage( $page, 'Hello \'\'World\'\'!' );
+
+ $parserOptions = $this->getParserOptions();
+ // With ParserOutputAccess::OPT_LINKS_UPDATE WikiPage::triggerOpportunisticLinksUpdate can be called
+ $this->installOpportunisticUpdateHook( true );
+
+ $status = $access->getParserOutput(
+ $page,
+ $parserOptions,
+ null,
+ ParserOutputAccess::OPT_LINKS_UPDATE | ParserOutputAccess::OPT_FOR_ARTICLE_VIEW
+ );
+ $this->assertContainsHtml( 'Hello <i>World</i>!', $status );
+ }
+
+ /**
* Tests that cached output in the ParserCache will be used for the latest revision.
*/
public function testLatestRevisionUseCached() {
@@ -327,7 +447,7 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
$page->clear();
- $access = $this->getParserOutputAccessNoCache();
+ $access = $this->getParserOutputAccess();
$parserOptions = $this->getParserOptions();
$status = $access->getParserOutput( $page, $parserOptions );
@@ -478,10 +598,49 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
}
/**
+ * Tests that getPageOutput() will generate output for an old revision, and
+ * that we still have the output for the current revision cached afterwards.
+ */
+ public function testOutputForOldRevisionUsingPoolCounter() {
+ $access = $this->getParserOutputAccess( [
+ 'expectPoolCounterCalls' => 2,
+ 'parserCache' => new HashBagOStuff(),
+ 'revisionOutputCache' => new HashBagOStuff()
+ ] );
+
+ $page = $this->getNonexistingTestPage( __METHOD__ );
+ $firstRev = $this->editPage( $page, 'First' )->getNewRevision();
+ $secondRev = $this->editPage( $page, 'Second' )->getNewRevision();
+
+ // output is for the second revision (write to ParserCache)
+ $parserOptions = $this->getParserOptions();
+ $status = $access->getParserOutput(
+ $page,
+ $parserOptions,
+ null,
+ ParserOutputAccess::OPT_FOR_ARTICLE_VIEW
+ );
+ $this->assertContainsHtml( 'Second', $status );
+
+ // output is for the first revision (not written to ParserCache)
+ $status = $access->getParserOutput(
+ $page,
+ $parserOptions,
+ $firstRev,
+ ParserOutputAccess::OPT_FOR_ARTICLE_VIEW
+ );
+ $this->assertContainsHtml( 'First', $status );
+
+ // Latest revision is still the one in the ParserCache
+ $output = $access->getCachedParserOutput( $page, $parserOptions );
+ $this->assertContainsHtml( 'Second', $output );
+ }
+
+ /**
* Tests that trying to get output for a suppressed old revision is denied.
*/
public function testOldRevisionSuppressedDenied() {
- $access = $this->getParserOutputAccessNoCache();
+ $access = $this->getParserOutputAccess();
$page = $this->getNonexistingTestPage( __METHOD__ );
$firstRev = $this->editPage( $page, 'First' )->getNewRevision();
@@ -504,7 +663,7 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
* is set.
*/
public function testOldRevisionSuppressedAllowed() {
- $access = $this->getParserOutputAccessNoCache();
+ $access = $this->getParserOutputAccess();
$page = $this->getNonexistingTestPage( __METHOD__ );
$firstRev = $this->editPage( $page, 'First' )->getNewRevision();
@@ -650,7 +809,7 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
* Tests that a RevisionRecord with no ID cannot be rendered if OPT_NO_CACHE is not set.
*/
public function testFakeRevisionError() {
- $access = $this->getParserOutputAccessNoCache();
+ $access = $this->getParserOutputAccess();
$parserOptions = $this->getParserOptions();
$page = $this->getExistingTestPage( __METHOD__ );
@@ -665,7 +824,7 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
* Tests that trying to render a RevisionRecord for another page will throw an exception.
*/
public function testPageIdMismatchError() {
- $access = $this->getParserOutputAccessNoCache();
+ $access = $this->getParserOutputAccess();
$parserOptions = $this->getParserOptions();
$page1 = $this->getExistingTestPage( __METHOD__ . '-1' );
@@ -679,7 +838,7 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
* Tests that trying to render a non-existing page will be reported as an error.
*/
public function testNonExistingPage() {
- $access = $this->getParserOutputAccessNoCache();
+ $access = $this->getParserOutputAccess();
$page = $this->getNonexistingTestPage( __METHOD__ );
@@ -728,13 +887,22 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
public function testPoolWorkDirty( $status, $fastStale, $expectedMessage ) {
MWTimestamp::setFakeTime( '2020-04-04T01:02:03' );
- $access = $this->getParserOutputAccessWithCache();
+ $access = $this->getParserOutputAccess( [
+ 'expectPoolCounterCalls' => 2,
+ 'parserCache' => new HashBagOStuff()
+ ] );
$page = $this->getNonexistingTestPage( __METHOD__ );
$this->editPage( $page, 'Hello \'\'World\'\'!' );
$parserOptions = $this->getParserOptions();
- $access->getParserOutput( $page, $parserOptions );
+ $access->getParserOutput(
+ $page,
+ $parserOptions,
+ null,
+ ParserOutputAccess::OPT_FOR_ARTICLE_VIEW
+ );
+
$testingAccess = TestingAccessWrapper::newFromObject( $access );
$testingAccess->localCache->clear();
@@ -749,7 +917,12 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
MWTimestamp::setFakeTime( '2020-05-05T01:02:03' );
$parserOptions = $this->getParserOptions();
- $cachedResult = $access->getParserOutput( $page, $parserOptions );
+ $cachedResult = $access->getParserOutput(
+ $page,
+ $parserOptions,
+ null,
+ ParserOutputAccess::OPT_FOR_ARTICLE_VIEW
+ );
$this->assertContainsHtml( 'World', $cachedResult );
$this->assertStatusWarning( $expectedMessage, $cachedResult );
@@ -767,13 +940,20 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
$this->setService( 'PoolCounterFactory',
$this->makePoolCounterFactory( Status::newGood( PoolCounter::TIMEOUT ) ) );
- $access = $this->getParserOutputAccessNoCache();
+ $access = $this->getParserOutputAccess( [
+ 'expectPoolCounterCalls' => 1
+ ] );
$page = $this->getNonexistingTestPage( __METHOD__ );
$this->editPage( $page, 'Hello \'\'World\'\'!' );
$parserOptions = $this->getParserOptions();
- $result = $access->getParserOutput( $page, $parserOptions );
+ $result = $access->getParserOutput(
+ $page,
+ $parserOptions,
+ null,
+ ParserOutputAccess::OPT_FOR_ARTICLE_VIEW
+ );
$this->assertStatusError( 'pool-timeout', $result );
}
@@ -787,7 +967,7 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
$this->setService( 'PoolCounterFactory',
$this->makePoolCounterFactory( Status::newFatal( 'some-error' ) ) );
- $access = $this->getParserOutputAccessNoCache();
+ $access = $this->getParserOutputAccess();
$page = $this->getNonexistingTestPage( __METHOD__ );
$this->editPage( $page, 'Hello \'\'World\'\'!' );
@@ -821,7 +1001,9 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
->method( 'getRevisionOutputCache' )
->willReturn( $revisionOutputCache );
- $access = $this->getParserOutputAccessWithCacheFactory( $parserCacheFactory );
+ $access = $this->getParserOutputAccess( [
+ 'parserCacheFactory' => $parserCacheFactory
+ ] );
$parserOptions0 = $this->getParserOptions();
$page = $this->getNonexistingTestPage( __METHOD__ );
$output = $access->getCachedParserOutput( $page, $parserOptions0 );
@@ -871,7 +1053,9 @@ class ParserOutputAccessTest extends MediaWikiIntegrationTestCase {
return $caches[$which];
} );
- $access = $this->getParserOutputAccessWithCacheFactory( $parserCacheFactory );
+ $access = $this->getParserOutputAccess( [
+ 'parserCacheFactory' => $parserCacheFactory
+ ] );
$page = $this->getNonexistingTestPage( __METHOD__ );
$firstRev = $this->editPage( $page, 'First __NOTOC__' )->getNewRevision();
$secondRev = $this->editPage( $page, 'Second __NOTOC__' )->getNewRevision();
diff --git a/tests/phpunit/includes/title/MediaWikiTitleCodecTest.php b/tests/phpunit/includes/title/MediaWikiTitleCodecTest.php
index ca73fd98f71e..ff2659a064c1 100644
--- a/tests/phpunit/includes/title/MediaWikiTitleCodecTest.php
+++ b/tests/phpunit/includes/title/MediaWikiTitleCodecTest.php
@@ -19,6 +19,7 @@
* @author Daniel Kinzler
*/
+use MediaWiki\Cache\GenderCache;
use MediaWiki\Interwiki\InterwikiLookup;
use MediaWiki\MainConfigNames;
use MediaWiki\Page\PageIdentity;
diff --git a/tests/phpunit/includes/title/TitleTest.php b/tests/phpunit/includes/title/TitleTest.php
index ce434da16eb0..f0963625044e 100644
--- a/tests/phpunit/includes/title/TitleTest.php
+++ b/tests/phpunit/includes/title/TitleTest.php
@@ -1,5 +1,6 @@
<?php
+use MediaWiki\Cache\BacklinkCache;
use MediaWiki\Language\RawMessage;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MainConfigNames;
diff --git a/tests/phpunit/includes/upload/UploadFromUrlTest.php b/tests/phpunit/includes/upload/UploadFromUrlTest.php
index da1aa7003c56..058963163027 100644
--- a/tests/phpunit/includes/upload/UploadFromUrlTest.php
+++ b/tests/phpunit/includes/upload/UploadFromUrlTest.php
@@ -292,4 +292,15 @@ class UploadFromUrlTest extends ApiTestCase {
$this->assertUploadOk( $upload );
}
+ public function testUploadFromUrlCacheKey() {
+ // Test we get back a properly formatted sha1 key out
+ $key = UploadFromUrl::getCacheKey( [ 'filename' => 'test.png', 'url' => 'https://example.com/example.png' ] );
+ $this->assertNotEmpty( $key );
+ $this->assertMatchesRegularExpression( "/^[0-9a-f]{40}$/", $key );
+ }
+
+ public function testUploadFromUrlCacheKeyMissingParam() {
+ $this->assertSame( "", UploadFromUrl::getCacheKey( [] ) );
+ }
+
}
diff --git a/tests/phpunit/includes/watcheditem/WatchedItemStoreUnitTest.php b/tests/phpunit/includes/watcheditem/WatchedItemStoreUnitTest.php
index 79b52db56572..00f53d5e0e1c 100644
--- a/tests/phpunit/includes/watcheditem/WatchedItemStoreUnitTest.php
+++ b/tests/phpunit/includes/watcheditem/WatchedItemStoreUnitTest.php
@@ -1,6 +1,8 @@
<?php
+use MediaWiki\Cache\GenderCache;
use MediaWiki\Cache\LinkBatchFactory;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Deferred\DeferredUpdates;
use MediaWiki\Linker\LinksMigration;
diff --git a/tests/phpunit/integration/includes/Permissions/RestrictionStoreTest.php b/tests/phpunit/integration/includes/Permissions/RestrictionStoreTest.php
index b2fdea819011..9a9000bf57bb 100644
--- a/tests/phpunit/integration/includes/Permissions/RestrictionStoreTest.php
+++ b/tests/phpunit/integration/includes/Permissions/RestrictionStoreTest.php
@@ -3,8 +3,8 @@
namespace MediaWiki\Tests\Integration\Permissions;
use IDBAccessObject;
-use LinkCache;
use MediaWiki\Cache\CacheKeyHelper;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\CommentStore\CommentStore;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\HookContainer\HookContainer;
diff --git a/tests/phpunit/integration/includes/Rest/Handler/Helper/HtmlOutputRendererHelperTest.php b/tests/phpunit/integration/includes/Rest/Handler/Helper/HtmlOutputRendererHelperTest.php
index 609f71d13b41..d845cf9d8755 100644
--- a/tests/phpunit/integration/includes/Rest/Handler/Helper/HtmlOutputRendererHelperTest.php
+++ b/tests/phpunit/integration/includes/Rest/Handler/Helper/HtmlOutputRendererHelperTest.php
@@ -2,7 +2,6 @@
namespace MediaWiki\Tests\Rest\Handler\Helper;
-use BagOStuff;
use CssContent;
use EmptyBagOStuff;
use Exception;
@@ -21,6 +20,8 @@ use MediaWiki\Page\PageRecord;
use MediaWiki\Page\ParserOutputAccess;
use MediaWiki\Parser\ParserCacheFactory;
use MediaWiki\Parser\ParserOutput;
+use MediaWiki\Parser\Parsoid\HtmlTransformFactory;
+use MediaWiki\Parser\Parsoid\LanguageVariantConverter;
use MediaWiki\Parser\Parsoid\PageBundleParserOutputConverter;
use MediaWiki\Parser\Parsoid\ParsoidOutputAccess;
use MediaWiki\Parser\Parsoid\ParsoidParser;
@@ -46,6 +47,7 @@ use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
+use Wikimedia\Bcp47Code\Bcp47Code;
use Wikimedia\Bcp47Code\Bcp47CodeValue;
use Wikimedia\Message\MessageValue;
use Wikimedia\Parsoid\Core\ClientError;
@@ -244,19 +246,9 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
return $authority;
}
- /**
- * @param BagOStuff|null $cache
- * @param ?ParsoidOutputAccess $access
- *
- * @return HtmlOutputRendererHelper
- * @throws Exception
- */
- private function newHelper(
- BagOStuff $cache = null,
- ?ParsoidOutputAccess $access = null
- ): HtmlOutputRendererHelper {
+ private function newHelper( array $options = [] ): HtmlOutputRendererHelper {
$chFactory = $this->getServiceContainer()->getContentHandlerFactory();
- $cache = $cache ?: new EmptyBagOStuff();
+ $cache = $options['cache'] ?? new EmptyBagOStuff();
$stash = new SimpleParsoidOutputStash( $chFactory, $cache, 1 );
$services = $this->getServiceContainer();
@@ -264,8 +256,8 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
$helper = new HtmlOutputRendererHelper(
$stash,
new NullStatsdDataFactory(),
- $access ?? $this->newMockParsoidOutputAccess(),
- $services->getHtmlTransformFactory(),
+ $options['ParsoidOutputAccess'] ?? $this->newMockParsoidOutputAccess(),
+ $options['HtmlTransformFactory'] ?? $services->getHtmlTransformFactory(),
$services->getContentHandlerFactory(),
$services->getLanguageFactory()
);
@@ -368,7 +360,7 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
// Use the real ParsoidOutputAccess, so we use the real hook container.
$access = $this->getServiceContainer()->getParsoidOutputAccess();
- $helper = $this->newHelper( null, $access );
+ $helper = $this->newHelper( [ 'ParsoidOutputAccess' => $access ] );
$helper->init( $page, self::PARAM_DEFAULTS, $this->newAuthority() );
// Do it.
@@ -429,7 +421,7 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
$cache = new HashBagOStuff();
- $helper = $this->newHelper( $cache );
+ $helper = $this->newHelper( [ 'cache' => $cache ] );
$helper->init( $page, self::PARAM_DEFAULTS, $this->newAuthority() );
$helper->setStashingEnabled( true );
@@ -449,7 +441,7 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
$page = $this->getNonexistingTestPage();
$cache = new HashBagOStuff();
- $helper = $this->newHelper( $cache );
+ $helper = $this->newHelper( [ 'cache' => $cache ] );
$text = 'just some wikitext';
@@ -561,7 +553,7 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
$cache = new HashBagOStuff();
// First, test it works if nothing was cached yet.
- $helper = $this->newHelper( $cache );
+ $helper = $this->newHelper( [ 'cache' => $cache ] );
$helper->init( $page, self::PARAM_DEFAULTS, $this->newAuthority(), $rev );
// put HTML into the cache
@@ -594,7 +586,7 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
DeferredUpdates::doUpdates();
$page->clear();
- $helper = $this->newHelper( $cache );
+ $helper = $this->newHelper( [ 'cache' => $cache ] );
$helper->init( $page, self::PARAM_DEFAULTS, $this->newAuthority(), $rev );
$this->assertStringNotContainsString( $renderId->getKey(), $helper->getETag() );
@@ -627,7 +619,7 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
return Status::newGood( $pout );
} );
- $helper = $this->newHelper( null, $poa );
+ $helper = $this->newHelper( [ 'ParsoidOutputAccess' => $poa ] );
$helper->init( $fakePage, self::PARAM_DEFAULTS, $this->newAuthority() );
$helper->setRevision( $fakeRevision );
@@ -679,7 +671,7 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
$cache = new HashBagOStuff();
// First, test it works if nothing was cached yet.
- $helper = $this->newHelper( $cache );
+ $helper = $this->newHelper( [ 'cache' => $cache ] );
$helper->init( $page, $params + self::PARAM_DEFAULTS, $this->newAuthority() );
$etag = $helper->getETag( $mode );
@@ -687,6 +679,72 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
$this->assertStringEndsWith( $suffix, $etag );
}
+ public static function provideVariantConversionLanguage() {
+ yield 'simple code'
+ => [ 'en', new Bcp47CodeValue( 'en' ) ];
+
+ yield 'code with dashes'
+ => [ 'en-x-piglatin', new Bcp47CodeValue( 'en-x-piglatin' ) ];
+
+ yield 'obsolete alias'
+ => [ 'zh-min-nan', new Bcp47CodeValue( 'nan' ) ];
+
+ yield 'obsolete alias in source language'
+ => [ 'en', new Bcp47CodeValue( 'en' ),
+ 'zh-min-nan', new Bcp47CodeValue( 'nan' ) ];
+
+ yield 'target and source given as objects'
+ => [ new Bcp47CodeValue( 'x y z' ), new Bcp47CodeValue( 'x y z' ),
+ new Bcp47CodeValue( 'a,b,c' ), new Bcp47CodeValue( 'a,b,c' ) ];
+
+ yield 'complex accept-language header (T350852)'
+ => [ 'da, en-gb;q=0.8, en;q=0.7', new Bcp47CodeValue( 'da' ) ];
+ }
+
+ /**
+ * @dataProvider provideVariantConversionLanguage
+ */
+ public function testSetVariantConversionLanguage(
+ $target,
+ Bcp47Code $expectedTarget,
+ $source = null,
+ ?Bcp47Code $expectedSource = null
+ ) {
+ $converter = $this->createNoOpMock(
+ LanguageVariantConverter::class,
+ [ 'convertPageBundleVariant' ]
+ );
+
+ // This is the key assertion in this test:
+ $converter->expects( $this->once() )
+ ->method( 'convertPageBundleVariant' )->with(
+ $this->anything(),
+ $expectedTarget,
+ $expectedSource
+ );
+
+ $transformFactory = $this->createNoOpMock(
+ HtmlTransformFactory::class,
+ [ 'getLanguageVariantConverter' ]
+ );
+ $transformFactory->method( 'getLanguageVariantConverter' )
+ ->willReturn( $converter );
+
+ $this->overrideConfigValue( MainConfigNames::UsePigLatinVariant, true );
+ $page = $this->getExistingTestPage( __METHOD__ );
+
+ $helper = $this->newHelper( [ 'HtmlTransformFactory' => $transformFactory ] );
+ $helper->init( $page, self::PARAM_DEFAULTS, $this->newAuthority() );
+
+ // call method under test
+ $helper->setVariantConversionLanguage( $target, $source );
+
+ // Secondary assertion, to ensure that the ETag varies on the right thing.
+ $this->assertStringEndsWith( "+lang:$expectedTarget\"", $helper->getETag() );
+
+ $helper->getPageBundle();
+ }
+
public static function provideHandlesParsoidError() {
yield 'ClientError' => [
new ClientError( 'TEST_TEST' ),
@@ -812,15 +870,14 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
$parsoid->method( 'wikitext2html' )
->willThrowException( $parsoidException );
- $parserCache = $this->createNoOpMock( ParserCache::class, [ 'get', 'makeParserOutputKey', 'getMetadata' ] );
+ $parserCache = $this->createNoOpMock( ParserCache::class, [ 'get', 'makeParserOutputKey' ] );
$parserCache->method( 'get' )->willReturn( false );
- $parserCache->expects( $this->once() )->method( 'getMetadata' );
$parserCache->expects( $this->atLeastOnce() )->method( 'makeParserOutputKey' );
$this->resetServicesWithMockedParsoid( $parsoid );
$access = $this->newRealParsoidOutputAccess( [ 'parserCache' => $parserCache ] );
- $helper = $this->newHelper( null, $access );
+ $helper = $this->newHelper( [ 'ParsoidOutputAccess' => $access ] );
$helper->init( $page, self::PARAM_DEFAULTS, $this->newAuthority() );
$this->expectExceptionObject( $expectedException );
@@ -835,10 +892,9 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
$page = PageIdentityValue::localIdentity( $page->getId(), $page->getNamespace(), $page->getDBkey() );
// This is the key assertion in this test case: get() and save() are both called.
- $parserCache = $this->createNoOpMock( ParserCache::class, [ 'get', 'save', 'getMetadata', 'makeParserOutputKey' ] );
+ $parserCache = $this->createNoOpMock( ParserCache::class, [ 'get', 'save', 'makeParserOutputKey' ] );
$parserCache->expects( $this->once() )->method( 'get' )->willReturn( false );
$parserCache->expects( $this->once() )->method( 'save' );
- $parserCache->expects( $this->once() )->method( 'getMetadata' );
$parserCache->expects( $this->atLeastOnce() )->method( 'makeParserOutputKey' );
$this->resetServicesWithMockedParsoid();
@@ -847,7 +903,7 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
'revisionCache' => $this->createNoOpMock( RevisionOutputCache::class )
] );
- $helper = $this->newHelper( null, $access );
+ $helper = $this->newHelper( [ 'ParsoidOutputAccess' => $access ] );
$helper->init( $page, self::PARAM_DEFAULTS, $this->newAuthority() );
$helper->getHtml();
@@ -858,9 +914,8 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
// NOTE: The save() method is not supported and will throw!
// The point of this test case is asserting that save() isn't called.
- $parserCache = $this->createNoOpMock( ParserCache::class, [ 'get', 'getMetadata', 'makeParserOutputKey' ] );
+ $parserCache = $this->createNoOpMock( ParserCache::class, [ 'get', 'makeParserOutputKey' ] );
$parserCache->method( 'get' )->willReturn( false );
- $parserCache->expects( $this->once() )->method( 'getMetadata' );
$parserCache->expects( $this->atLeastOnce() )->method( 'makeParserOutputKey' );
$this->resetServicesWithMockedParsoid();
@@ -869,7 +924,7 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
'revisionCache' => $this->createNoOpMock( RevisionOutputCache::class ),
] );
- $helper = $this->newHelper( null, $access );
+ $helper = $this->newHelper( [ 'ParsoidOutputAccess' => $access ] );
$helper->init( $page, self::PARAM_DEFAULTS, $this->newAuthority() );
// Set read = true, write = false
@@ -883,9 +938,8 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
// NOTE: The get() method is not supported and will throw!
// The point of this test case is asserting that get() isn't called.
// We also check that save() is still called.
- $parserCache = $this->createNoOpMock( ParserCache::class, [ 'save', 'getMetadata', 'makeParserOutputKey' ] );
+ $parserCache = $this->createNoOpMock( ParserCache::class, [ 'save', 'makeParserOutputKey' ] );
$parserCache->expects( $this->once() )->method( 'save' );
- $parserCache->expects( $this->once() )->method( 'getMetadata' );
$parserCache->expects( $this->atLeastOnce() )->method( 'makeParserOutputKey' );
$this->resetServicesWithMockedParsoid();
@@ -894,7 +948,7 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
'revisionCache' => $this->createNoOpMock( RevisionOutputCache::class ),
] );
- $helper = $this->newHelper( null, $access );
+ $helper = $this->newHelper( [ 'ParsoidOutputAccess' => $access ] );
$helper->init( $page, self::PARAM_DEFAULTS, $this->newAuthority() );
// Set read = false, write = true
@@ -938,7 +992,7 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
return Status::newGood( $pout );
} );
- $helper = $this->newHelper( null, $poa );
+ $helper = $this->newHelper( [ 'ParsoidOutputAccess' => $poa ] );
$page = $this->getExistingTestPage();
@@ -1116,7 +1170,10 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
*/
public function testDummyContentForBadModel( string $flavor ) {
$this->resetServicesWithMockedParsoid();
- $helper = $this->newHelper( new HashBagOStuff(), $this->newRealParsoidOutputAccess() );
+ $helper = $this->newHelper( [
+ 'cache' => new HashBagOStuff(),
+ 'ParsoidOutputAccess' => $this->newRealParsoidOutputAccess()
+ ] );
$page = $this->getNonexistingTestPage( __METHOD__ );
$this->editPage( $page, new CssContent( '"not wikitext"' ) );
@@ -1157,7 +1214,7 @@ class HtmlOutputRendererHelperTest extends MediaWikiIntegrationTestCase {
return Status::newGood( $pout );
} );
- $helper = $this->newHelper( null, $poa );
+ $helper = $this->newHelper( [ 'ParsoidOutputAccess' => $poa ] );
$helper->init( $page, [], $this->newAuthority() );
$pb = $helper->getPageBundle();
$this->assertSame( $pb->version, Parsoid::defaultHTMLVersion() );
diff --git a/tests/phpunit/integration/includes/Rest/Handler/ParsoidHandlerTest.php b/tests/phpunit/integration/includes/Rest/Handler/ParsoidHandlerTest.php
index cce921a223a1..c38f9fb0fe6a 100644
--- a/tests/phpunit/integration/includes/Rest/Handler/ParsoidHandlerTest.php
+++ b/tests/phpunit/integration/includes/Rest/Handler/ParsoidHandlerTest.php
@@ -1323,7 +1323,13 @@ class ParsoidHandlerTest extends MediaWikiIntegrationTestCase {
if ( $expectedException instanceof HttpException ) {
/** @var HttpException $e */
- $this->assertSame( $expectedException->getErrorData(), $e->getErrorData() );
+ $this->assertSame(
+ $expectedException->getErrorData(),
+ array_intersect_key(
+ $expectedException->getErrorData(),
+ $e->getErrorData()
+ )
+ );
}
$this->assertSame( $expectedException->getMessage(), $e->getMessage() );
@@ -2076,14 +2082,13 @@ class ParsoidHandlerTest extends MediaWikiIntegrationTestCase {
$this->assertStringContainsString( 'Page 1 revision content', $data );
// Test 3 repeated with ParserCache to ensure nothing is written to cache!
- $parserCache = $this->createNoOpMock( ParserCache::class, [ 'save', 'get', 'makeParserOutputKey', 'getMetadata' ] );
+ $parserCache = $this->createNoOpMock( ParserCache::class, [ 'save', 'get', 'makeParserOutputKey' ] );
// This is the critical assertion -- no cache svaes for mismatched rev & page params
$parserCache->expects( $this->never() )->method( 'save' );
// Ensures there is a cache miss
$parserCache->method( 'get' )->willReturn( false );
// Verify that the cache is queried
$parserCache->expects( $this->atLeastOnce() )->method( 'makeParserOutputKey' );
- $parserCache->expects( $this->atLeastOnce() )->method( 'getMetadata' );
$parserCacheFactory = $this->createNoOpMock(
ParserCacheFactory::class,
[ 'getParserCache', 'getRevisionOutputCache' ]
@@ -2101,7 +2106,7 @@ class ParsoidHandlerTest extends MediaWikiIntegrationTestCase {
$page = $this->getExistingTestPage();
$pageConfig = $this->getPageConfig( $page );
- $parserCache = $this->createNoOpMock( ParserCache::class, [ 'save', 'get', 'makeParserOutputKey', 'getMetadata' ] );
+ $parserCache = $this->createNoOpMock( ParserCache::class, [ 'save', 'get', 'makeParserOutputKey' ] );
// This is the critical assertion in this test case: the save() method should
// be called exactly once!
@@ -2109,7 +2114,6 @@ class ParsoidHandlerTest extends MediaWikiIntegrationTestCase {
$parserCache->method( 'get' )->willReturn( false );
// These methods will be called by ParserOutputAccess:qa
$parserCache->expects( $this->atLeastOnce() )->method( 'makeParserOutputKey' );
- $parserCache->expects( $this->atLeastOnce() )->method( 'getMetadata' );
$parserCacheFactory = $this->createNoOpMock(
ParserCacheFactory::class,
@@ -2130,11 +2134,30 @@ class ParsoidHandlerTest extends MediaWikiIntegrationTestCase {
// This should trigger a parser cache write, because we didn't set a write-ratio
$handler->wt2html( $pageConfig, $attribs );
+ }
- $this->overrideConfigValue( 'TemporaryParsoidHandlerParserCacheWriteRatio', 0 );
+ public function testWt2html_variant_conversion() {
+ $page = $this->getExistingTestPage();
+ $pageConfig = $this->getPageConfig( $page );
- // This should not trigger a parser cache write, because we set the write-ration to 0
- $handler->wt2html( $pageConfig, $attribs );
+ $attribs = self::DEFAULT_ATTRIBS;
+ $attribs['opts']['from'] = 'wikitext';
+ $attribs['opts']['format'] = 'html';
+ $attribs['opts']['accept-language'] = 'en-x-piglatin';
+
+ $handler = $this->newParsoidHandler();
+
+ // This should trigger a parser cache write, because we didn't set a write-ratio
+ $response = $handler->wt2html( $pageConfig, $attribs );
+
+ $body = $response->getBody();
+ $body->rewind();
+ $data = $body->getContents();
+
+ $this->assertStringContainsString(
+ '<meta http-equiv="content-language" content="en-x-piglatin"/>',
+ $data
+ );
}
public function testWt2html_BadContentModel() {
diff --git a/tests/phpunit/integration/includes/block/DatabaseBlockStoreTest.php b/tests/phpunit/integration/includes/block/DatabaseBlockStoreTest.php
index 49f1d904f23b..ca65bcd19fe1 100644
--- a/tests/phpunit/integration/includes/block/DatabaseBlockStoreTest.php
+++ b/tests/phpunit/integration/includes/block/DatabaseBlockStoreTest.php
@@ -124,6 +124,10 @@ class DatabaseBlockStoreTest extends MediaWikiIntegrationTestCase {
$this->assertSame( $autoblock->isEmailBlocked(), $block->isEmailBlocked() );
$this->assertSame( $autoblock->isUsertalkEditAllowed(), $block->isUsertalkEditAllowed() );
$this->assertSame( $autoblock->isSitewide(), $block->isSitewide() );
+ $this->assertSame(
+ $autoblock->getReasonComment()->text,
+ wfMessage( 'autoblocker', $block->getTargetName(), $block->getReasonComment()->text )->text()
+ );
$restrictionStore = $this->getServiceContainer()->getBlockRestrictionStore();
$this->assertTrue(
@@ -470,15 +474,23 @@ class DatabaseBlockStoreTest extends MediaWikiIntegrationTestCase {
public function testUpdateBlock() {
$store = $this->getStore();
$existingBlock = $store->newFromTarget( $this->sysop );
+
+ // Insert an autoblock for T351173 regression testing
+ $autoblockId = $store->doAutoblock( $existingBlock, '127.0.0.1' );
+
+ // Modify a block option
$existingBlock->isUsertalkEditAllowed( true );
+ $newExpiry = wfTimestamp( TS_MW, time() + 1000 );
+ $existingBlock->setExpiry( $newExpiry );
$result = $store->updateBlock( $existingBlock );
$updatedBlock = $store->newFromID( $result['id'] );
- $autoblock = $store->newFromID( $result['autoIds'][0] );
+ $autoblock = $store->newFromID( $autoblockId );
$this->assertTrue( $updatedBlock->equals( $existingBlock ) );
$this->assertAutoblockEqualsBlock( $existingBlock, $autoblock );
+ $this->assertLessThanOrEqual( $newExpiry, $autoblock->getExpiry() );
}
public function testUpdateBlockAddOrRemoveAutoblock() {
diff --git a/tests/phpunit/integration/includes/cache/HtmlCacheUpdaterIntegrationTest.php b/tests/phpunit/integration/includes/cache/HtmlCacheUpdaterIntegrationTest.php
index 3e5e2bf3dee4..a336d832271d 100644
--- a/tests/phpunit/integration/includes/cache/HtmlCacheUpdaterIntegrationTest.php
+++ b/tests/phpunit/integration/includes/cache/HtmlCacheUpdaterIntegrationTest.php
@@ -1,5 +1,6 @@
<?php
+use MediaWiki\Cache\HTMLCacheUpdater;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\HookContainer\StaticHookRegistry;
use MediaWiki\Page\PageReference;
@@ -16,11 +17,11 @@ use Wikimedia\Rdbms\FakeResultWrapper;
class HtmlCacheUpdaterIntegrationTest extends MediaWikiIntegrationTestCase {
/**
- * @return HtmlCacheUpdater
+ * @return HTMLCacheUpdater
* @throws Exception
*/
- private function newHtmlCacheUpdater(): HtmlCacheUpdater {
- $updater = new HtmlCacheUpdater(
+ private function newHtmlCacheUpdater(): HTMLCacheUpdater {
+ $updater = new HTMLCacheUpdater(
new HookContainer(
new StaticHookRegistry(),
$this->getServiceContainer()->getObjectFactory()
@@ -97,7 +98,7 @@ class HtmlCacheUpdaterIntegrationTest extends MediaWikiIntegrationTestCase {
/**
* @dataProvider providePurgeTitleUrls
- * @covers \HtmlCacheUpdater::purgeTitleUrls
+ * @covers \MediaWiki\Cache\HTMLCacheUpdater::purgeTitleUrls
*/
public function testPurgeTitleUrls( $pages, $expected ) {
$this->setService( 'EventRelayerGroup', $this->getEventRelayGroup( $expected ) );
@@ -107,7 +108,7 @@ class HtmlCacheUpdaterIntegrationTest extends MediaWikiIntegrationTestCase {
}
/**
- * @covers \HtmlCacheUpdater::purgeUrls
+ * @covers \MediaWiki\Cache\HTMLCacheUpdater::purgeUrls
*/
public function testPurgeUrls() {
$urls = [ 'https://acme.test/wiki/Foo', 'https://acme.test/wiki/Bar', ];
diff --git a/tests/phpunit/maintenance/GetLagTimesTest.php b/tests/phpunit/maintenance/GetLagTimesTest.php
index 1f12ee2e83f2..77fab9c99046 100644
--- a/tests/phpunit/maintenance/GetLagTimesTest.php
+++ b/tests/phpunit/maintenance/GetLagTimesTest.php
@@ -29,7 +29,8 @@ class GetLagTimesTest extends MaintenanceBaseTestCase {
'No lag' => [ 0, '/localhost\s+0 $/m' ],
'Some lag' => [ 7, '/localhost\s+7 \*{7}$/m' ],
'More than 40 seconds lag' => [ 41, '/localhost\s+41 \*{40}$/m' ],
- 'Not replicating' => [ false, '/localhost\s+0 $/m' ],
+ 'Not replicating' => [
+ false, '/localhost\s+0 replication stopped or errored$/m' ],
];
}
diff --git a/tests/phpunit/mocks/DummyServicesTrait.php b/tests/phpunit/mocks/DummyServicesTrait.php
index 90b564b39588..81cc93198457 100644
--- a/tests/phpunit/mocks/DummyServicesTrait.php
+++ b/tests/phpunit/mocks/DummyServicesTrait.php
@@ -21,11 +21,11 @@
namespace MediaWiki\Tests\Unit;
-use GenderCache;
use Interwiki;
use InvalidArgumentException;
use Language;
use MediaWiki\Cache\CacheKeyHelper;
+use MediaWiki\Cache\GenderCache;
use MediaWiki\CommentFormatter\CommentParser;
use MediaWiki\CommentFormatter\CommentParserFactory;
use MediaWiki\CommentStore\CommentStore;
diff --git a/tests/phpunit/unit/includes/CommentFormatter/CommentParserFactoryTest.php b/tests/phpunit/unit/includes/CommentFormatter/CommentParserFactoryTest.php
index 9f5fa4101858..9823855e9ac7 100644
--- a/tests/phpunit/unit/includes/CommentFormatter/CommentParserFactoryTest.php
+++ b/tests/phpunit/unit/includes/CommentFormatter/CommentParserFactoryTest.php
@@ -21,8 +21,8 @@
namespace MediaWiki\Tests\Unit\CommentFormatter;
use Language;
-use LinkCache;
use MediaWiki\Cache\LinkBatchFactory;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\CommentFormatter\CommentParser;
use MediaWiki\CommentFormatter\CommentParserFactory;
use MediaWiki\HookContainer\HookContainer;
diff --git a/tests/phpunit/unit/includes/Permissions/PermissionManagerTest.php b/tests/phpunit/unit/includes/Permissions/PermissionManagerTest.php
index 09fc8e2f1e41..bc8640f97847 100644
--- a/tests/phpunit/unit/includes/Permissions/PermissionManagerTest.php
+++ b/tests/phpunit/unit/includes/Permissions/PermissionManagerTest.php
@@ -5,6 +5,7 @@ namespace MediaWiki\Tests\Unit\Permissions;
use MediaWiki\Actions\ActionFactory;
use MediaWiki\Block\BlockErrorFormatter;
use MediaWiki\Block\BlockManager;
+use MediaWiki\Cache\UserCache;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\MainConfigNames;
@@ -21,7 +22,6 @@ use MediaWiki\User\User;
use MediaWiki\User\UserFactory;
use MediaWiki\User\UserGroupManager;
use MediaWikiUnitTestCase;
-use UserCache;
use Wikimedia\TestingAccessWrapper;
/**
diff --git a/tests/phpunit/unit/includes/Permissions/RestrictionStoreTest.php b/tests/phpunit/unit/includes/Permissions/RestrictionStoreTest.php
index 2d4100f50c42..b2975e140cbc 100644
--- a/tests/phpunit/unit/includes/Permissions/RestrictionStoreTest.php
+++ b/tests/phpunit/unit/includes/Permissions/RestrictionStoreTest.php
@@ -3,7 +3,7 @@
namespace MediaWiki\Tests\Unit\Permissions;
use DatabaseTestHelper;
-use LinkCache;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Linker\LinksMigration;
use MediaWiki\MainConfigNames;
diff --git a/tests/phpunit/unit/includes/Rest/Handler/HandlerTest.php b/tests/phpunit/unit/includes/Rest/Handler/HandlerTest.php
index 2c7eff7a7acc..1052f9f685cc 100644
--- a/tests/phpunit/unit/includes/Rest/Handler/HandlerTest.php
+++ b/tests/phpunit/unit/includes/Rest/Handler/HandlerTest.php
@@ -472,7 +472,10 @@ class HandlerTest extends MediaWikiUnitTestCase {
return [
'null body type' => [
new RequestData(),
- null
+ new LocalizedHttpException(
+ new MessageValue( 'rest-unsupported-content-type', [ '' ] ),
+ 415
+ )
],
'json body' => [
new RequestData( [
@@ -485,7 +488,10 @@ class HandlerTest extends MediaWikiUnitTestCase {
new RequestData( [
'headers' => [ 'Content-Type' => 'unknown/type' ]
] ),
- null
+ new LocalizedHttpException(
+ new MessageValue( 'rest-unsupported-content-type', [ '' ] ),
+ 415
+ )
],
'invalid json body' => [
new RequestData( [
@@ -505,6 +511,8 @@ class HandlerTest extends MediaWikiUnitTestCase {
$handler = $this->newHandler();
if ( $expectedResult instanceof LocalizedHttpException ) {
$this->expectException( LocalizedHttpException::class );
+ $this->expectExceptionCode( $expectedResult->getCode() );
+ $this->expectExceptionMessage( $expectedResult->getMessage() );
$handler->parseBodyData( $requestData );
} else {
$parsedBody = $handler->parseBodyData( $requestData );
diff --git a/tests/phpunit/unit/includes/Rest/RequestBaseTest.php b/tests/phpunit/unit/includes/Rest/RequestBaseTest.php
index 28c907b602c7..b988c382e8b3 100644
--- a/tests/phpunit/unit/includes/Rest/RequestBaseTest.php
+++ b/tests/phpunit/unit/includes/Rest/RequestBaseTest.php
@@ -64,4 +64,34 @@ class RequestBaseTest extends \MediaWikiUnitTestCase {
$rb->setHeaders( [ 'Content-type' => 'application/json' ] );
$this->assertSame( [ 'application/json' ], $rb->getHeaders()[ 'Content-type' ] );
}
+
+ public static function provideHasBody() {
+ yield 'nothing'
+ => [ [], false ];
+
+ yield 'content-length: 1'
+ => [ [ 'content-length' => '1' ], true ];
+
+ yield 'content-length: 0'
+ => [ [ 'content-length' => '0' ], true ];
+
+ yield 'content-length empty'
+ => [ [ 'content-length' => '' ], false ];
+
+ yield 'transfer-encoding: chunked'
+ => [ [ 'transfer-encoding' => 'chunked' ], true ];
+
+ yield 'transfer-encoding empty'
+ => [ [ 'transfer-encoding' => '' ], false ];
+ }
+
+ /**
+ * @dataProvider provideHasBody
+ */
+ public function testHasBody( $headers, $expected ) {
+ $rb = $this->getMockForAbstractClass( RequestBase::class, [ 'cookiePrefix' ] );
+ $rb->setHeaders( $headers );
+ $this->assertSame( $expected, $rb->hasBody() );
+ }
+
}
diff --git a/tests/phpunit/unit/includes/Rest/RequestDataTest.php b/tests/phpunit/unit/includes/Rest/RequestDataTest.php
index 315f3c08e287..cb9211c974a3 100644
--- a/tests/phpunit/unit/includes/Rest/RequestDataTest.php
+++ b/tests/phpunit/unit/includes/Rest/RequestDataTest.php
@@ -221,4 +221,47 @@ class RequestDataTest extends \MediaWikiUnitTestCase {
$this->assertSame( $expectedResult, $request->getBodyType() );
}
+ public static function provideHasBody() {
+ yield 'nothing'
+ => [ [], false ];
+
+ yield 'content-length: 1'
+ => [ [ 'content-length' => '1' ], true ];
+
+ yield 'content-length: 0'
+ => [ [ 'content-length' => '0' ], true ];
+
+ yield 'content-length empty'
+ => [ [ 'content-length' => '' ], false ];
+
+ yield 'transfer-encoding: chunked'
+ => [ [ 'transfer-encoding' => 'chunked' ], true ];
+
+ yield 'transfer-encoding empty'
+ => [ [ 'transfer-encoding' => '' ], false ];
+ }
+
+ /**
+ * @dataProvider provideHasBody
+ */
+ public function testHasBodyBasedOnHeader( $headers, $expected ) {
+ $request = new RequestData( [
+ 'headers' => $headers
+ ] );
+ $this->assertSame( $expected, $request->hasBody() );
+ }
+
+ public function testHasBodyWithContent() {
+ $request = new RequestData( [
+ 'bodyContents' => 'test test test'
+ ] );
+ $this->assertTrue( $request->hasBody() );
+ }
+
+ public function testHasBodyWithParsedBody() {
+ $request = new RequestData( [
+ 'parsedBody' => [ 'foo' => 'bar' ]
+ ] );
+ $this->assertTrue( $request->hasBody() );
+ }
}
diff --git a/tests/phpunit/unit/includes/cache/HtmlCacheUpdaterTest.php b/tests/phpunit/unit/includes/cache/HTMLCacheUpdaterTest.php
index d3e18acc26d1..a5d777b67967 100644
--- a/tests/phpunit/unit/includes/cache/HtmlCacheUpdaterTest.php
+++ b/tests/phpunit/unit/includes/cache/HTMLCacheUpdaterTest.php
@@ -1,17 +1,18 @@
<?php
+use MediaWiki\Cache\HTMLCacheUpdater;
use MediaWiki\Title\Title;
use MediaWiki\Title\TitleFactory;
use PHPUnit\Framework\MockObject\MockObject;
/**
* @group Cache
- * @covers \HtmlCacheUpdater
+ * @covers \MediaWiki\Cache\HTMLCacheUpdater
*/
-class HtmlCacheUpdaterTest extends MediaWikiUnitTestCase {
+class HTMLCacheUpdaterTest extends MediaWikiUnitTestCase {
public function testGetCdnUrls() {
- $htmlCache = new HtmlCacheUpdater(
+ $htmlCache = new HTMLCacheUpdater(
$this->createHookContainer(),
$this->createTitleFactory(),
0, false, 86400 );
diff --git a/tests/phpunit/unit/includes/cache/LinkBatchFactoryTest.php b/tests/phpunit/unit/includes/cache/LinkBatchFactoryTest.php
index de975917376e..6cad101d3fef 100644
--- a/tests/phpunit/unit/includes/cache/LinkBatchFactoryTest.php
+++ b/tests/phpunit/unit/includes/cache/LinkBatchFactoryTest.php
@@ -1,6 +1,9 @@
<?php
+use MediaWiki\Cache\GenderCache;
+use MediaWiki\Cache\LinkBatch;
use MediaWiki\Cache\LinkBatchFactory;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Linker\LinksMigration;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\Page\PageReference;
diff --git a/tests/phpunit/unit/includes/libs/objectcache/HashBagOStuffTest.php b/tests/phpunit/unit/includes/libs/objectcache/HashBagOStuffTest.php
index 337e1720c249..f63d11a9010b 100644
--- a/tests/phpunit/unit/includes/libs/objectcache/HashBagOStuffTest.php
+++ b/tests/phpunit/unit/includes/libs/objectcache/HashBagOStuffTest.php
@@ -5,9 +5,15 @@ namespace Wikimedia\Tests\ObjectCache;
use BagOStuff;
use HashBagOStuff;
use InvalidArgumentException;
-use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
use MediaWikiCoversValidator;
+use NullStatsdDataFactory;
use PHPUnit\Framework\TestCase;
+use Psr\Log\NullLogger;
+use UDPTransport;
+use Wikimedia\Stats\Metrics\MetricInterface;
+use Wikimedia\Stats\OutputFormats;
+use Wikimedia\Stats\StatsCache;
+use Wikimedia\Stats\StatsFactory;
use Wikimedia\TestingAccessWrapper;
/**
@@ -165,20 +171,34 @@ class HashBagOStuffTest extends TestCase {
* Ensure updateOpStats doesn't get confused.
*/
public function testUpdateOpStats() {
- $counts = [];
-
- $stats = $this->createMock( StatsdDataFactoryInterface::class );
- $stats->method( 'updateCount' )->willReturnCallback(
- static function ( $name, $delta ) use ( &$counts ) {
- $counts[$name] = ( $counts[$name] ?? 0 ) + $delta;
- }
+ $statsCache = new StatsCache();
+ $emitter = OutputFormats::getNewEmitter(
+ 'mediawiki',
+ $statsCache,
+ OutputFormats::getNewFormatter( OutputFormats::DOGSTATSD )
);
+ $transport = $this->createMock( UDPTransport::class );
+ $transport->expects( $this->once() )->method( "emit" )
+ ->with(
+ "mediawiki.bagostuff_call_total:1|c|#keygroup:Foo,operation:frob
+mediawiki.bagostuff_call_total:1|c|#keygroup:Bar,operation:frob
+mediawiki.bagostuff_call_total:1|c|#keygroup:UNKNOWN,operation:frob
+mediawiki.bagostuff_bytes_sent_total:5|c|#keygroup:Bar,operation:frob
+mediawiki.bagostuff_bytes_sent_total:5|c|#keygroup:UNKNOWN,operation:frob
+mediawiki.bagostuff_bytes_read_total:3|c|#keygroup:Bar,operation:frob
+mediawiki.bagostuff_bytes_read_total:3|c|#keygroup:UNKNOWN,operation:frob
+mediawiki.stats_buffered_total:7|c\n"
+ );
+ $emitter = $emitter->withTransport( $transport );
+ $stats = new StatsFactory( $statsCache, $emitter, new NullLogger );
+
+ $stats->withStatsdDataFactory( new NullStatsdDataFactory() );
$cache = new HashBagOStuff( [
'stats' => $stats
] );
- $cache = TestingAccessWrapper::newFromObject( $cache );
+ $cache = TestingAccessWrapper::newFromObject( $cache );
$cache->updateOpStats(
'frob',
[
@@ -193,13 +213,11 @@ class HashBagOStuffTest extends TestCase {
]
);
- $this->assertSame( 1, $counts['objectcache.Foo.frob_call_rate'] );
- $this->assertSame( 1, $counts['objectcache.Bar.frob_call_rate'] );
- $this->assertSame( 1, $counts['objectcache.UNKNOWN.frob_call_rate'] );
+ /** @var MetricInterface[] $metrics */
+ $metrics = ( TestingAccessWrapper::newFromObject( $stats ) )->cache->getAllMetrics();
+ $this->assertCount( 3, $metrics );
- $this->assertSame( 3, $counts['objectcache.Bar.frob_bytes_read'] );
- $this->assertSame( 5, $counts['objectcache.Bar.frob_bytes_sent'] );
- $this->assertSame( 3, $counts['objectcache.UNKNOWN.frob_bytes_read'] );
- $this->assertSame( 5, $counts['objectcache.UNKNOWN.frob_bytes_sent'] );
+ // send metrics
+ $stats->flush();
}
}
diff --git a/tests/phpunit/unit/includes/linker/LinkRendererFactoryTest.php b/tests/phpunit/unit/includes/linker/LinkRendererFactoryTest.php
index 7bc0e0055e18..0b0f204b4a70 100644
--- a/tests/phpunit/unit/includes/linker/LinkRendererFactoryTest.php
+++ b/tests/phpunit/unit/includes/linker/LinkRendererFactoryTest.php
@@ -1,5 +1,6 @@
<?php
+use MediaWiki\Cache\LinkCache;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\Linker\LinkRenderer;
use MediaWiki\Linker\LinkRendererFactory;
diff --git a/tests/phpunit/unit/includes/page/PageStoreFactoryTest.php b/tests/phpunit/unit/includes/page/PageStoreFactoryTest.php
index c7cfe5d0f331..b2f67a98027f 100644
--- a/tests/phpunit/unit/includes/page/PageStoreFactoryTest.php
+++ b/tests/phpunit/unit/includes/page/PageStoreFactoryTest.php
@@ -1,8 +1,8 @@
<?php
namespace MediaWiki\Tests\Page;
-use LinkCache;
use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
+use MediaWiki\Cache\LinkCache;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\MainConfigNames;
use MediaWiki\Page\PageStore;
diff --git a/tests/phpunit/unit/includes/parser/Parsoid/LanguageVariantConverterUnitTest.php b/tests/phpunit/unit/includes/parser/Parsoid/LanguageVariantConverterUnitTest.php
index 8bc0d410afe9..d5e5bc26e49f 100644
--- a/tests/phpunit/unit/includes/parser/Parsoid/LanguageVariantConverterUnitTest.php
+++ b/tests/phpunit/unit/includes/parser/Parsoid/LanguageVariantConverterUnitTest.php
@@ -117,50 +117,50 @@ class LanguageVariantConverterUnitTest extends MediaWikiUnitTestCase {
public static function provideSourceLanguage() {
yield 'content-language in PageBundle' => [
- 'sr', // PageBundle content-language
- null, // Title PageLanguage
- null, // PageLanguage override
+ 'sr', // PageBundle content-language
+ null, // Title PageLanguage
+ null, // PageLanguage override
'sr-Cyrl', // target
'sr-Cyrl', // explicit source
'sr-Cyrl' // expected source
];
yield 'content-language but no source language' => [
- 'en', // PageBundle content-language
- null, // Title PageLanguage
- null, // PageLanguage override
- 'en', // target
- null, // explicit source
+ 'en', // PageBundle content-language
+ null, // Title PageLanguage
+ null, // PageLanguage override
+ 'en', // target
+ null, // explicit source
null // expected source
];
yield 'content-language is variant' => [
'en-ca', // PageBundle content-language
- null, // Title PageLanguage
- null, // PageLanguage override
- 'en', // target
- null, // explicit source
+ null, // Title PageLanguage
+ null, // PageLanguage override
+ 'en', // target
+ null, // explicit source
'en-ca' // expected source
];
yield 'Source variant is given' => [
- null, // PageBundle content-language
- null, // Title PageLanguage
- null, // PageLanguage override
- 'en', // target
+ null, // PageBundle content-language
+ null, // Title PageLanguage
+ null, // PageLanguage override
+ 'en', // target
'en-ca', // explicit source
'en-ca' // expected source
];
yield 'Source variant is a base language' => [
- null, // PageBundle content-language
- null, // Title PageLanguage
- null, // PageLanguage override
- 'en', // target
- 'en', // explicit source
+ null, // PageBundle content-language
+ null, // Title PageLanguage
+ null, // PageLanguage override
+ 'en', // target
+ 'en', // explicit source
null // expected source
];
yield 'Page language override is variant' => [
- null, // PageBundle content-language
- null, // PageBundle content-language
+ null, // PageBundle content-language
+ null, // PageBundle content-language
'en-ca', // PageLanguage override
- 'en', // target
+ 'en', // target
'en-ca', // explicit source
'en-ca' // expected source
];